I'm attempting to implement the interface IDispatchMessageInspector (of WCF fame) in F#:
open System.ServiceModel.Dispatcher
open System.ServiceModel.Channels
type ServiceInterceptor() as interceptor =
abstract member PreInvoke : byref<Message> -> obj
abstract member PostInvoke : byref<Message> -> obj -> unit
default x.PreInvoke m = null
default x.PostInvoke m s = ()
interface IDispatchMessageInspector with
member x.AfterReceiveRequest(request, channel, instanceContext) = interceptor.PreInvoke(&request)
member x.BeforeSendReply(reply : byref<Message>, correlationState) = interceptor.PostInvoke &reply correlationState
This fails to compile with the following error:

However, if I modify my code to the following (note the change of signature in PostInvoke) everything works:
open System.ServiceModel.Dispatcher
open System.ServiceModel.Channels
type ServiceInterceptor() as interceptor =
abstract member PreInvoke : byref<Message> -> obj
abstract member PostInvoke : byref<Message> * obj -> unit
default x.PreInvoke m = null
default x.PostInvoke (m, s) = ()
interface IDispatchMessageInspector with
member x.AfterReceiveRequest(request, channel, instanceContext) = interceptor.PreInvoke(&request)
member x.BeforeSendReply(reply : byref<Message>, correlationState) = interceptor.PostInvoke(&reply, correlationState)
Is this behaviour expected? And if so could someone explain the reasoning behind it....
The reason is that byref<'T> is not a real type in .NET. F# uses this for representing values that are passed via ref and out parameters, but it is not a normal type that could appear anywhere in your program.
F# restricts the scope in which they can be used - you can only use them for local variables (basically passing around a reference or a pointer) and you can use them as method parameters (where the compiler can then compile it as a method parameter).
With curried methods, the compiler is producing a property that returns a function value and so (under the cover), you get something like a property PostInvoke of type FSharpFunc<T1, FSharpFunc<T2, T3>>. And here, T1 or T2 cannot be byref<T> types, because byref is not a real .NET type. So that's why curried methods cannot have byref parameters.
Another case where you can see this is if you, for example, try to create a list of byref values:
let foo () =
let a : list<byref<int>> = []
a
Here you get:
error FS0412: A type instantiation involves a byref type. This is not permitted by the rules of Common IL.
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