Passing arguments by reference in curried arguments - 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:
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.

Related

byref return in F# 4.5

I am trying to add a F#-style interface to a type, that has a byref return method.
Here's the code:
type IPool<'P, 'T when 'T: struct> =
abstract member GetReference: ITypedPointer<'P, 'T> -> byref<'T>
let Ref<'TPool, 'P, 'T when 'TPool :> IPool<'P, 'T>> (pool: 'TPool) pointer =
pool.GetReference pointer
Now to my surprise, a similar thing worked fine until I introduced IPool interface. Before that Ref itself contained an implementation like &pool.data.[idx], and worked fine.
I tried installing nightly build of F# Tools, cause latest release does not officially support byref returns, and PR to introduce them was recently completed: https://github.com/Microsoft/visualfsharp/pull/4888
However, I still get error FS3209: The address of the variable 'copyOfStruct' cannot be used at this point. A method or function may not return the address of this local value. in Visual Studio. Type outref<T> still does not seem to be available either. Am I missing something?
I also tried to drop the pointer parameter, and just return pool.GetReference to only get a different error message.
Addition: the ultimate goal is to be able to do
let aref = Ref pool ptr
let bref = Ref pool ptr
aref <- 42
assert(aref = bref)
e.g. give caller a direct reference to an internal memory, usually backed by an array, similar to Span<T>. I am making this for performance reasons, so it is not OK to allocate on every call to Ref.
For some reason, reducing generalization helped to get rid of the error:
let Ref<'P, 'T when 'T: struct> (pool: IPool<'P, 'T>) pointer = pool.GetReference pointer
Solution provided by
https://github.com/Microsoft/visualfsharp/issues/5366#issuecomment-407521220
Though it does not explain why the original code does not compile.
I don't think it's standard practice to return a byref type. This type is really meant for method parameters, mostly for C# interop with out or ref parameters. Take a look at this StackOverflow question for a good explanation.
What you can do is change the method on your interface to take a tuple of ITypedPointer<'P,'T> and byref<'T> (usage of byref is not allowed with curried parameters) and return unit instead. Then you can call GetReference like any standard .NET method with an out parameter in C#. That would look something like this:
type ITypedPointer<'P, 'T> = interface end
type IPool<'P, 'T when 'T: struct> =
abstract member GetReference: ITypedPointer<'P, 'T> * byref<'T> -> unit
let Ref<'TPool, 'P, 'T when 'TPool :> IPool<'P, 'T>> (pool: 'TPool) pointer =
let mutable value = Unchecked.defaultof<'T>
pool.GetReference(pointer, &value)
value

Nested Generic Type Inference in f#

In the following code I define two interfaces, the second of which takes the first as a type parameter. However the code gives the error "type parameter 'a' is not defined".
type IFirst<'a> =
abstract Data : 'a
type ISecond<'First when 'First :> IFirst<'a>> =
abstract First : 'First
abstract SomeData : 'a
My question is why can't f# infer what type 'a' is when ISecond is derived, since the information is embedded in 'First'?
For example in the following code the compiler could infer that 'a' is a string.
type First () =
interface IFirst<string> with
member x.Data = ""
type Second () =
interface ISecond<First> with
member x.SomeData = ""
member x.First = First()
Is there any way around this or does ISecond have to take two type parameters?
EDIT: I Am aware that ISecond can take two type parameters (note the last line of my initial question). To make it clearer what I mean consider the following code
type IFirst<'a> = interface end
type ISecond<'First, 'a when 'First :> IFirst<'a>> = interface end
type First () =
interface IFirst<string>
type Second () =
interface ISecond<First, int>
It gives the error "This expression was expected to have type string but here has type int", meaning the compiler knows that 'a' is a string, yet I still have to declare it as such. I wish to know why this is the case and whether there is a workaround without specifying the second type parameter.
Your definition of ISecond is incorrect: you're mentioning some type 'a, but not defining it. In other words, ISecond actually has two generic parameters - 'First and 'a, but you've only defined one of them.
This would work:
type ISecond<'a, 'First when 'First :> IFirst<'a>> =
abstract First : 'First
abstract SomeData : 'a
But then, of course, you'll need to amend your definition of Second as well:
type Second () =
interface ISecond<string, First> with
member x.SomeData = ""
member x.First = First()
I think you have conflated two different questions. One is:
Do a type definition's generic parameters all need to be explicit?
The answer is yes. This has nothing to do with type inference, it's just how type definitions work in F# - the type parameter 'a will only be in scope to be used as an argument to IFirst<_> if it's also a parameter of ISecond.
The other question is:
Can the compiler infer a super-type's type parameters when defining a subtype?
Here, the answer is slightly more subtle. When defining a class type, the answer is that it is a syntactic requirement that you specify all of the type parameters. If you try something like:
type Second() = interface ISecond<First,_> with ...
you'll get the error message
error FS0715: Anonymous type variables are not permitted in this declaration
However, there are other contexts where the parameters can be inferred without issue:
let second() = { new ISecond<_,_> with
member x.SomeData = ""
member x.First = First() }
Here you can see that when using an object expression both of the parameters can be inferred.

What would be the inferred type of my function here?

I am wondering why F# compiler allows the following
type MyMath() =
member this.Add a b = a + b
What would be the type of Add method and its arguments ? If I compiled this into the Library and try to use it in C# what types of arguments it is going to expect ?
Shouldn't the F# require you to explicitly specify types when it comes to Methods of classes ?
You can enter the code in F# interactive and see the inferred type yourself:
> type MyMath() =
member this.Add a b = a + b;;
type MyMath =
class
new : unit -> MyMath
member Add : a:int -> b:int -> int
end
Here, the compiler uses default type for the + operator which is int. The operator can be used with other types, but the inference uses int as the default. You can use type annotations, but you are not required to do that if you are happy with the inferred type.
In general, you can use type annotations in F# to specify types if you want to, but in many cases, the inferred type will be exactly what you want, so you do not have to make the code more verbose, if the inference behaves as expected.
Of course, if you were writing some library and wanted to be super careful about changing the API, then you might want to use type annotations (or you can add F# Interface file .fsi)
F# Interactive is your friend:
type MyMath =
class
new : unit -> MyMath
member Add : a:int -> b:int -> int
end

Why can't a function with byref be converted directly to delegate?

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

why is the implementation of my abstract member not public

I've been struggling to get this to compile for about an hour. It must be something stupid. Can you spot it?
in my lib project:
namespace TravelerStuff
open System
type Traveler =
abstract GetData : unit -> unit
type public DeltaTraveler() =
interface Traveler with
member v.GetData () =
printf "hello"
and in my console test app:
[<EntryPoint>] let main _ =
let traveler = new TravelerStuff.DeltaTraveler()
traveler.GetData // this line won't compile: (The field, constructor or member 'GetData' is not defined)
As gradbot says, F# doesn't currently implicitly convert values to interfaces when searching for members. Also, F# only uses explicit interface implementation (as known from C#) and not implicit implementation where members are not only compiled as implementation of an interface, but also as ordinary (directly visible) members of the type.
Aside from casting, you can duplicate the member in the type definition:
type DeltaTraveler() =
member v.GetData () = printf "hello"
interface Traveler with
member v.GetData () = v.GetData()
Also, if you just want to implement an interface, but don't need to add any members, you can use F# object expressions (which are more lightweight):
let deltaTraveler() =
{ new Traveler with
member v.GetData () = printf "hello" }
// The function directly returns value of type 'Traveler'
let t = deltaTraveler()
t.GetData()
You need to upcast. F# currently won't do it for you in this situation.
(traveler :> TravelerStuff.Traveler).GetData()
// open the namespace to reduce typing.
open TravelerStuff
(traveler :> Traveler).GetData()
Snip from F# docs.
In many object-oriented languages,
upcasting is implicit; in F#, the
rules are slightly different.
Upcasting is applied automatically
when you pass arguments to methods on
an object type. However, for let-bound
functions in a module, upcasting is
not automatic, unless the parameter
type is declared as a flexible type.
For more information, see Flexible Types (F#).

Resources