Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing large sequences as function parameters

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.

like image 832
Shredderroy Avatar asked Dec 04 '22 03:12

Shredderroy


2 Answers

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.

like image 173
Brian Avatar answered Dec 22 '22 21:12

Brian


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.

like image 32
Tomas Petricek Avatar answered Dec 22 '22 19:12

Tomas Petricek