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.