Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing arguments by reference in curried arguments

Tags:

f#

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:

enter image description here

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

like image 344
Lawrence Avatar asked Feb 01 '26 12:02

Lawrence


1 Answers

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.

like image 90
Tomas Petricek Avatar answered Feb 03 '26 09:02

Tomas Petricek



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!