Question

Under normal circumstances, F# functions can be converted to delegates by calling new DelegateType and passing in the function as an argument. But when the delegate contains byref parameter, this is not possible directly. For example the code:

type ActionByRef<'a> = delegate of 'a byref -> unit

let f (x:double byref) = 
    x <- 6.0

let x = ref 42.0
let d = new ActionByRef<_>(f)

won't compile, giving the following error:

This function value is being used to construct a delegate type whose signature includes a byref argument. You must use an explicit lambda expression taking 1 arguments.

Following the error, modifying the code to use

let d = new ActionByRef<_>(fun x -> f(&x))

works. But my question is: why is this necessary? Why won't F# allow the conversion from named function to this delegate, but conversion from lambda is fine?

I came upon this behavior when researching another question. I realize byref is meant only for compatibility with other .Net languages.

Was it helpful?

Solution

I think the problem is that byref<'T> is not an actual type in F# - it looks like a type (to make the language simpler), but it gets compiled to a parameter marked with the out flag. This means that byref<'T> can be only used in a place where the compiler can actually use the out flag.

The problem with function values is that you can construct function e.g. by partial application:

let foo (n:int) (b:byref<int>) = 
  b <- n

When you pass foo as an argument to a delegate constructor, it is a specific case of partial application (with no arguments), but partial application actually needs to construct a new method and then give that to the delegate:

type IntRefAction = delegate of byref<int> -> unit  

let ac = IntRefAction(foo 5)

The compiler could be clever and generate new method with byref parameter (or out flag) and then pass that by reference to the actual function, but in general, there will be other compiler-generated method when you don't use the fun ... -> ... syntax. Handling this would add complexity and I think that's a relatively rare case, so the F# compiler doesn't do that and asks you to be more explicit...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top