Understanding byref, ref and &
Asked Answered
L

1

29

Well, I came to understand that F# is able to manage references (some sort of C++ like references). This enables the possibilities to change value of parameters passed in functions and also enables the programmer to return more than a single value. However here's what I need to know:

  1. Ref keyword: The keyword ref is used to create, from a value, a reference to that value of the inferred type. So

    let myref = ref 10
    

    This means that F# will create an object of type Ref<int> putting there (in the mutable field) my int 10.

    OK. So I assume that ref is used to create instances of the Ref<'a> type. Is it correct?

  2. Access value: In order to access a value stored in reference I can do this:

    let myref = ref 10
    let myval = myref.Value
    let myval2 = !myref
    

    While the := operator just lets me edit the value like this:

    let myref = ref 10
    myref.Value <- 30
    myref := 40
    

    So ! (Bang) dereferences my reference. And := edit it. I suppose this is correct too.

  3. The & operator: What does this operator do? Is it to be applied to a reference type? No, I guess it must be applied to a mutable value and this returns what? The reference? The address? If using interactive:

    let mutable mutvar = 10;;
    &a;;
    

    The last line throws an error so I do not understand what the & operator is for.

  4. ByRef: What about byref? That's very important to me, but I realize I do not understand it. I understand it is used in function regarding parameter passing. One uses byref when he wants that the passed value can be edited (this is a bit against the functional languages' philosophy but f# is something more than that). Consider the following:

    let myfunc (x: int byref) =
        x <- x + 10
    

    This is strange. I know that if you have a reference let myref = ref 10 and then do this to edit the value: myref <- 10 it arises an error because it should be like this: myref := 10. However, the fact that in that function I can edit x using the <- operator means that x is not a reference, right?

    If I assume that x is not a reference, then I assume also that, in functions, when using byref on a parameter, that parameter can have the mutable syntax applied to. So it is just a matter of syntax, if I assume this I am OK, and, in fact, everything works (no compiler errors). However, what is x?

  5. Calling functions: How can I use a function utilizing byref parameters?

    The & operator is involved but could you explain this better please? In this article: MSDN Parameters and Arguments the following example is provided:

    type Incrementor(z) =
        member this.Increment(i : int byref) =
           i <- i + z
    
    let incrementor = new Incrementor(1)
    let mutable x = 10
    // A: Not recommended: Does not actually increment the variable. (Me: why?)
    incrementor.Increment(ref x)
    // Prints 10.
    printfn "%d" x  
    
    let mutable y = 10
    incrementor.Increment(&y) (* Me: & what does it return? *)
    // Prints 11.
    printfn "%d" y 
    
    let refInt = ref 10
    incrementor.Increment(refInt) (* Why does it not work in A, but here it does? *)
    // Prints 11.
    printfn "%d" !refInt
    
Letterpress answered 17/2, 2011 at 11:32 Comment(1)
Style tips: multiple question marks do not make your questions more "question-y" and they make you look a little silly: one is enough. Text in all caps is hard to read and is generally understood to mean that you're shouting: use formatting (like italic and bold) for emphasis.Battlement
S
32

Ref keyword Yes, when you write let a = ref 10 you're essentially writing let a = new Ref<int>(10) where the Ref<T> type has a mutable field Value.

Access value The := and ! operators are just shortcuts for writing:

a.Value <- 10  // same as writing: a := 10
a.Value        // same as writing: !a

ByRef is a special type that can be (reasonably) used only in method parameters. It means that the argument should be essentially a pointer to some memory location (allocated on heap or stack). It corresponds to out and ref modifiers in C#. Note that you cannot create local variable of this type.

The & operator is a way to create a value (a pointer) that can be passed as an argument to a function/method expecting a byref type.

Calling functions the example with byref works because you're passing the method a reference to a local mutable variable. Via the reference, the method can change the value stored in that variable.

The following doesn't work:

let a = 10            // Note: You don't even need 'mutable' here
bar.Increment(ref a)  

The reason is that you're creating a new instance of Ref<int> and you're copying the value of a into this instance. The Increment method then modifies the value stored on heap in the instance of Ref<int>, but you don't have a reference to this object anymore.

let a = ref 10
bar.Increment(a)  

This works, because a is a value of type Ref<int> and you're passing a pointer to the heap-allocated instance to Increment and then get the value from heap-allocated reference cell using !a.

(You can use values created using ref as arguments for byref because the compiler handles this case specially - it will automatically take reference of the Value field because this is a useful scenario...).

Stoush answered 17/2, 2011 at 11:54 Comment(4)
Hoever I'm sorry Tomas, but when passing a ref to a function accepting a byref, the interpreter goes crazy...Letterpress
@Andry: "crazy" in what sense? Behavior you didn't expect or some error message? (I'll be happy to clarify that part..)Stoush
Sorry for being late... Well it tells me that expects a ByRef<'a> but gets a Ref<'a>... that's what is gets me... The interpreter just does not accepts that argument :(Letterpress
Just seen this as I was facing the same problem, If we have a byref<> in a win32 extern API then it requires a "&" operator to tell the compiler about the byref, otherwise as Tomas has said it should work with ref variables, let mutable pid = 0u GetWindowThreadProcessId(new IntPtr(phwnd),& pid) |> ignoreBridgeport

© 2022 - 2024 — McMap. All rights reserved.