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.
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...