0

I am wondering how passing reference and value types as arguments works under the hood and how that plays out in closures.

I was wondering how to deal with the risk of duplicating instances. Also, I may have to deal with large immutable data structures in the future.

I have found only hints on the use of mutable and reference cells. And again here. But this case I am interested in immutable value and reference types.

I tried to experiment but I did not figure out how to find out the address location of stuff which is not mutable

let myfun x  = 
    printfn "pointer: %A" &x

results in the error:

error FS0256: A value must be mutable in order to mutate the contents or take the address of a value type, e.g. 'let mutable x = ...'


Is the variable copied when passed as a parameter? I suppose that, dealing with immutable values, it would make no sense to "copy" argument values.

Case 1: Value type

let a = 10
let myfun x =
    printfn "x = %i" x
myfun a 

Case 2: Reference type

type Test () = 
    member this.DoSomething () = printfn "Hello!"
let useTestType (x: Test) = x.DoSomething ()    
let testInstance = Test ()
useTestType testInstance

Case 3: Class with a mutable

type Test2 () =
    let mutable counter = 10
    member this.DoSomething () = printfn "Hello!"
let useTestType2 (x: Test2) = x.DoSomething ()

Case 4: Closure - No, it is not copied.

type Test3 () =
    let mutable counter = 10
    member this.DoSomething () = printfn "Hello!"
    member this.Counter = counter
    member this.AddCounter x = 
        counter <- counter + x

let ex = Test3 ()
let closure (x: Test3) = (fun variable ->
                            x.DoSomething ()
                            variable + x.Counter )

let funct = closure ex
let res = funct 20

ex.AddCounter 20
let res2 = funct 20

Case 5: Other

Are the edge cases I should keep in mind?


For the big picture, this toy example simplifies one of issues I have in mind:

In this toy example, we have a DataDispatcher class. The class contains an Event that can be triggered. The idea of the class is that it will take care of the gathering and aggregation of data. A MaiboxProcessor can subscribe and get data delivered.

let names = [ "Sensor1"; "Sensor2"; "Sensor3" ]
let bdays = [ "CCH15"; "BBH15"; "AAH15" ]
let dict = List.zip names bdays |> Map.ofList


type MsgToAgent = | Value of string []


let testAgent =
    MailboxProcessor.Start(fun inbox ->
        let rec loop () =
            async { 
                    let! msg = inbox.Receive()
                    match msg with
                    | Value array -> 
                        printfn "Array: %s" (String.concat ", " array)
                    return! loop ()}
        printfn "Hello World"
        loop ())



type DataDispatcher (dictionary) =     

    let available = new Event<Map<string,string>> ()  

    member this.ManualTrigger () = 
        available.Trigger dictionary
    member this.Subscribe (agent: MailboxProcessor<MsgToAgent>, names: string []) = 
        available.Publish.Add (fun dict ->
            let list = 
                names 
                |> Array.map dict.TryFind 
                |> Array.choose id
            agent.Post (Value list) )

let datadispatcher = DataDispatcher(dict)
datadispatcher.Subscribe (testAgent, [| "Sensor1"; "Sensor3" |]) 
datadispatcher.ManualTrigger () 

I suspected that this implementation would duplicate the MailboxProcessor, leading to a proliferation of agents active and looping in the memory. However, screen output suggests this is not the case.

Community
  • 1
  • 1
NoIdeaHowToFixThis
  • 4,484
  • 2
  • 34
  • 69
  • 1
    Have you taken a look at http://msdn.microsoft.com/en-us/library/t63sy5hs.aspx – Aron Mar 05 '14 at 10:34
  • Yes, thanks. The link does not answer the question. – NoIdeaHowToFixThis Mar 05 '14 at 10:36
  • 2
    Ah...closures. Nothing is magical about closures. They are just syntaxic sugar, where a class is created to wrap the lambda, and the variables that are closed over are passed into the new class. The way it is passed in still depends on the same rules. HOWEVER its not always apparent what is being closed over, and quick often its `this` that gets closed over. This is to reduce the number of new classes being created. – Aron Mar 05 '14 at 10:40
  • I dont' fully understand your example. Are there references? Thank you. Btw, I added some few code lines, some starting from a little bit farther back but I'd like to clarify in my head what happens. – NoIdeaHowToFixThis Mar 05 '14 at 13:04

1 Answers1

1

I understand the main question to be:

"Is [a] variable copied when passed as a parameter?"

If the variable in question is of value type, then yes, unless you specifically use the "byref" construct (see here).

If the variable in question is of reference type, then no, the reference is copied, not the referenced object itself. It makes no difference whether or not the object is immutable.

So in general, like in C#, you won't duplicate instances simply by passing them as arguments. (The function receiving the argument could of course manually duplicate its argument, say, by invoking a copy constructor.)

Søren Debois
  • 5,598
  • 26
  • 48