f# static member constraint with multiple tupled arguments - f#

From f# I'm trying to call a function on a C# defined object using a member constraint. Since the c# member function take multiple arguments the f# compiler treats it as a tuple but when applying the generic member constraint I get an error that the function takes 4 arguments but I've only supplied one.
I've tried forming the tuple from the arguments or just taking the pre-tupled set of arguments but both give the same error. I think I must be defining my member constraint incorrectly but there aren't a lot of examples of member constraints with multiple arguments.
let inline wrapForDecode (buffer:DirectBuffer) (offset:int) (blockLen:uint16) (version:uint16) : ^a =
let msg = new ^a()
(^a : (member WrapForDecode : (DirectBuffer*int*int*int)->unit) msg, (buffer, offset, (int blockLen), (int version)))
msg
let inline wrapForDecode2 (args :DirectBuffer*int*int*int) : ^a =
let msg = new ^a()
(^a : (member WrapForDecode : (DirectBuffer*int*int*int)->unit) (msg, args))
msg
The original WrapForDecode member function is defined in c# like:
public void WrapForDecode(DirectBuffer buffer, int offset, int actingBlockLength, int actingVersion) {...}
When I try to call the function I get the following error for either wrapForDecode or wrapForDecode2.
The member or object constructor 'WrapForDecode' takes 4 argument(s) but is here given 1. The required signature is 'MDInstrumentDefinitionFuture27.WrapForDecode(buffer: DirectBuffer, offset: int, actingBlockLength: int, actingVersion: int) : unit'.

If you change the type of WrapForDecode's argument from (DirectBuffer*int*int*int) to DirectBuffer*int*int*int the first inline method will compile:
let inline wrapForDecode (buffer:string)
(offset:int)
(blockLen:uint16)
(version:uint16)
: ^a =
let msg = new ^a()
(^a : (member WrapForDecode : string * int * int * int -> unit)
msg,
buffer,
offset,
(int blockLen),
(int version))
msg
type foo () =
member this.WrapForDecode (a : string, b: int, c: int, d:int) =
()
let x : foo = wrapForDecode "asd" 1 2us 3us
In ordinary F# code, the two signatures would be equivalent - all methods take a single argument, and to write a function with arity > 1 it must either be curried or take a tupled argument.
However, that is not how the CLI works - in C#/VB.Net land, foo1(x : bar, y : baz) has a different signature from foo2(xy : Tuple<bar, baz>).
Normally the F# compiler automatically translates between the two styles, and so when accessing non-F# .NET code from F# you will see both methods as taking a tupled argument.
But statically-resolved member constraints are a complicated and relatively fringe feature of F#, so it appears that this automatic translation isn't or cannot be performed when invoking methods this way.
(thanks to #ildjarn for pointing out the source of this difference)

Related

Simplify SRTP resolved on return type

I have some production code that I'd like to simplify (especially in light of new SRTP F# behaviour).
The intention is to statically resolve a method/function based solely on the return type required.
A simplified version of it is this:
type TypeOf<'a> = T
type ZeroFactory = Z with
static member Zero(_: ZeroFactory,_ : TypeOf<int>) : _ = 0
static member Zero(_: ZeroFactory,_ : TypeOf<string>) : _ = ""
let inline inlineZero t =
((^T or ^N) : (static member Zero : ^T * TypeOf< ^N > -> ^N)
(t, T))
let inline zero () = inlineZero Z
let foo : int = zero ()
let bar : string = zero ()
this code compiles and does what I intend, but has always felt overly contrived.
I CAN write this:
let inline inlineZero2 t =
(^T : (static member Zero : ^T * TypeOf< ^N > -> ^N)
(t, T))
and to my eyes, that would seem to be good enough, but if i write:
let inline zero2 () = inlineZero2 Z
I get
Error FS0043 A unique overload for method 'Zero' could not be determined based on type information prior to this program point. A type annotation may be needed.
Known return type: 'a
Known type parameters: < ZeroFactory , TypeOf<'a> >
Candidates:
- static member ZeroFactory.Zero: ZeroFactory * TypeOf<int> -> int
- static member ZeroFactory.Zero: ZeroFactory * TypeOf<string> ->
my hunch is that all the static type parameters in the method specification have to be mentioned on the left.

Currying issues with F#. What is the right way to attach functions to the type?

I can't understand what is wrong with following bit of code:
let toClass (problem:Problem<'a>) (classID:int) (items:'a list) =
let newFreqTable = (problem.FreqTables.[classID]).count items
{ problem with FreqTables = newFreqTable :: (problem.FreqTables |> List.filter (fun i -> i.ClassID <> classID)) }
type Problem<'a> when 'a : equality with member this.toClass (classID:int) (items:list<'a>) = toClass this classID items
I have a Problem type which is nothing but a way to group up any number of FreqTables - short for "Frequency tables". So toClass method just takes appropriate freqTable (by classID argument) and returns a new one - with calculated given items.
let typeIndependentCall = toClass p 0 ["word"; "word"; "s"] // this works perfectly
let typeDependentCall = typeIndependentCall.toClass 1 ["word"; "s"]
// gives an error: "One or more of the overloads of this method has
// curried arguments. Consider redesigning these members to take
// arguments in tupled form".
I am pretty new to F# and functional programming. What is the right way to attach behavior to my type?
In F# there are 2 main ways of passing arguments to a function: curried and tupled. The curried form is what you are using in your code above, and has a few key benefits, the first and foremost being partial application.
For example, instead of thinking of
fun add a b = a + b
as a function that takes in 2 arguments and returns a value, we can think of it as a function of one argument that returns a function that with one argument. This is why the type signature of our function is
Int -> Int -> Int
or, more clearly,
Int -> (Int -> Int)
However, when overloading methods, we can only use the tupled argument form
(Int, Int) -> Int
The reason for this is for optimization, as is discussed here
To get your code to work, use
type Problem<'a> when 'a : equality with member this.toClass (classID:int, items:list<'a>) = toClass this classID items
and call it like such:
let typeDependentCall = typeIndependentCall.toClass(1, ["word"; "s"])

Why doesn't type inference determine automatically that property exists on type?

I need to compare objects of different types which I know all have properties Id and Legacy_id. Unfortunately I cannot add interface to them since the types come from database schema. I hoped the following comparer would work:
type Comparer<'T >()=
interface System.Collections.Generic.IEqualityComparer<'T> with
member this.Equals (o1:'T,o2:'T)=
o1.Legacy_id=o2.Legacy_id
member this.GetHashCode(o:'T)=
o.Id+o.Legacy_id
Also I have instantiations of the comparer type with the types. So, theoretically compiler has enough information.
But it gives an error: "Lookup on object of indeterminate type based on information prior to this program point. A type annotation may be needed prior to this program point to constrain the type of the object. This may allow the lookup to be resolved."
I wonder why F# fails here? Are there any real/theoretical restrictions or it is just not implemented? Such kind of inference could be very useful.
I suspect that an explanation is about F# compiler is only forward walking. The limitation C# doesn't have. That's what error message is complaining about. Is that so?
Member constraints cannot be used on types, which is why you can't do this. See here.
What you could do is create a comparer class that accepts an explicit equality-checking and hash code generation function for a specific type, e.g.
type Comparer<'T>(equalityFunc, hashFunc) =
interface System.Collections.Generic.IEqualityComparer<'T> with
member this.Equals (o1:'T,o2:'T)=
equalityFunc o1 o2
member this.GetHashCode(o:'T)=
hashFunc o
Then you could use an inline function to generate instances of the above for types that match the constraints you wish to impose:
let inline id obj =
( ^T : (member Id : int) (obj))
let inline legacyId obj =
( ^T : (member Legacy_id : int) (obj))
let inline equals o1 o2 =
id o1 = id o2
let inline hash o =
id o + legacyId o
let inline createComparer< ^T when ^T : (member Id: int) and ^T : (member Legacy_id : int) >() =
Comparer< ^T >(equals, hash) :> System.Collections.Generic.IEqualityComparer< ^T >
Say you have some type TestType that has the two required properties:
type TestType =
member this.Legacy_id = 7
member this.Id = 9
You can then do, for example, createComparer<TestType>() to generate an equality comparer appropriate to your type.
A concise way of getting a comparer for e.g. creating a HashSet is:
let inline getId o = (^T : (member Id : int) o)
let inline getLegacyId o = (^T : (member Legacy_id : int) o)
let inline legacyComparer< ^T when ^T : (member Id : int) and ^T : (member Legacy_id : int)>() =
{ new System.Collections.Generic.IEqualityComparer<'T> with
member __.GetHashCode o = getId o + getLegacyId o
member __.Equals(o1, o2) = getLegacyId o1 = getLegacyId o2 }
Type inference is about inferring a type, not proving that some generic type parameter satisfies arbitrary conditions for all instantiations (see nominal vs. structural type systems). There are many good answers on statically resolved type parameters on SO already.

F#: Implementing an interface with overloaded members

I have defined an interface in F# with an overloaded method. As per compiler request, the overload uses tupled arguments instead of curried ones:
type IInterface =
abstract member Do : (int * string) -> unit
abstract member Do : int -> unit
I then create a class which implements the interface:
type ImplementingClass =
interface IInterface with
member this.Do (i, s) = ()
member this.Do i = ()
However, doing so yields the compiler error for the first of both methods: "This override takes a different number of arguments to the corresponding abstract member"
What am I doing wrong here?
There is a subtle difference between the following two:
abstract member Do : int * string -> unit
abstract member Do : (int * string) -> unit
If you add the parentheses, you're saying that the parameter is a tuple and the compiler should produce a method taking Tuple<int, string>. Without parentheses, the method will be compiled as taking two parameters. Most of the time, this is hidden and you can ignore it - but sadly, not always.
So, you can either change your interface definition to use ordinary "two-parameter" method (this would be my preferred method - you can still call the method with tuple as an argument and looks nicer in the .NET/C# view):
type IInterface =
abstract member Do : int * string -> unit
abstract member Do : int -> unit
type ImplementingClass =
interface IInterface with
member this.Do (i, s) = ()
member this.Do i = ()
Or you can implement the interface as it is:
type ImplementingClass =
interface IInterface with
member this.Do((i:int, s:string)) = ()
member this.Do(i:int) = ()
Sadly, this is a bit ugly - you need the type annotations so that the compiler can unambiguously decide which method you're implementing.

How to define and use % as a prefix operator?

type T() =
static member (~%)(t : T) = t
let t = T()
let t' = %t // FAILS
The error message says t was expected to be of type Quotation.Expr<'a>.
% is a supposedly valid prefix operator, but is it possible to actually use it?
The reason why you are seeing this behavior is because F# does not define (~%) with static constraints like most top-level operators. It is defined as a function Quotations.Expr<'a> -> 'a. Hence, the (~%) function (which is an alias for op_Splice) you defined on type T is not resolved by uses of the top-level (~%) operator.
You can see this by the following FSI interaction:
> <# (~%) #>;;
<# (~%) #>;;
^^^^^^^^^^
C:\Users\Stephen\AppData\Local\Temp\stdin(5,1): error FS0030: Value restriction. The value 'it' has been inferred to have generic type
val it : Expr<(Expr<'_a> -> '_a)>
Either define 'it' as a simple data term, make it a function with explicit arguments or, if you do not intend for it to be generic, add a type annotation.
Thus if we redefine the top-level (~%) operator as follows, then your example will compile without error:
let inline (~%) (x : ^a) = (^a : (static member op_Splice : ^a -> 'b) (x))
but do note that quotation splicing will no longer work:
let x = <# 3 #>
<# %x #>
----^
error FS0001: The type 'Expr<int>' does not support the operator '~%'
that's because the original definition of (~%) is treated specially by the compiler for quotation splicing. Indeed, you can see in the Expr and Expr<'T> signatures that those types do not define any operators at all, let alone op_Splice.
You can see similar results with && and || infix operators. Which can be redefined (mapping to op_BooleanAnd and op_BooleanOr), but unless they are, they are treated specially by the compiler.
I'm not exactly sure why the % operator behaves like this, but you can redefine it using global let binding:
let (~%) a = -a
%10
If the operator cannot be defined as static member (I'm not sure if that's the case, or if I'm just missing something), you can still define an inline definition that invokes some static member of an object. This should give you essentially the same functionality:
// Instead of defining static member '%', we define static member 'Percent'
type T() =
static member Percent(t : T) = t
// Inline definition of '~%' that calls the static member 'Percent' of an object
let inline (~%) (x : ^T) = (^T : (static member Percent : ^T -> 'R) (x))
// Now you can use the '%t' syntax to invoke the static member
let t = T()
let t' = %t
Background: In F# quotation code, it is used for "splicing" of expressions into another expression (to build an expression that is composed from another, previously defined expression). The error message suggests that the compiler did not see your definition.
let two = <# 2 #>
let oneAndTwo = <# 1 + %two #>

Resources