This is a simple question really (but I couldn't seem to find the answer in the MSDN documents).
If I pass large sequences and objects as function parameters in F#, are they always copied by value unless I supply the byref keyword? The thing is, I have no intention of modifying the arguments, but at the same time, I don't want the large objects to be copied each time I call the function.
I disagree with the terminology people are using here.
I would phrase it as: everything in .NET is pass-by-value by default, the exceptions are e.g. ref
and out
parameters in C# and byref
parameters in F#. However, sequences and arrays and most other types (aparts from structs and primitives) are object references, which means when you pass them as arguments (by value), you are passing only a reference to the entity, and the caller and callee share the same actual entity on the heap. So the references are being passed by value, but the references themselves are just small things, whereas the object entities they point to (that contain all the data) are large things that are shared on the heap.
As mentioned in the comments, F# uses essentially the same mechanism as C#. This means that only value types (primitive values such as int
, float
and structs) are copied on a stack and all other types are passed by reference - this includes all collections (sequences seq<'T>
, arrays 'T[]
as well as immutable functional lists list<'T>
).
When you're working with immutable types (functional list<'T>
and seq<'T>
without side-effects), there is no difference between passing a reference and copying the object aside from performance - the runtime always passes them as reference, but you don't need to worry about this unless you're optimizing some program (if you could change the passing mechanism, it would not change the behaviour).
For (mutable) arrays, you can easily see that the behaviour is pass-by-reference by looking at the exampe from John Palmer's answer.
As a sidenote, the byref
keyword (or type) is used if you want to create a reference to a stack-allocated mutable variable. This means, you can use it to pass reference to a variable of a value type:
let bar (a:byref<int>) = // takes reference to a (stack-allocated) 'int' variable
a <- 42 // mutate the variable
let mutable foo = 10 // declare a mutable variable
bar &foo // pass reference to the `bar` function
If you were passing collections using byref
, you'd be actually passing a reference to a stack-allocated reference that actually points to the heap-allocated data structure. However, byref
is used very rarely in F# - you'll mainly need it for interoperability when calling C/C++ or COM library.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With