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.
Related
Whilst attempting to learn the use of constructor constraints, I was hoping that something like the following was possible.
type Foo<'T when 'T : (new : int -> 'T)> = {Bar: 'T}
But this does not compile, simply returning the error
'new' constraints must take one argument of type 'unit' and return the constructed type
It seems as though the constraint should be called "parameterless constructor constraint" because I cant get any other form beyond this to compile.
type Foo<'T when 'T : (new : unit-> 'T)> = {Bar: 'T}
Can a constructor constraint be used to constrain the generic types constructor to have a specific signature other than unit ?
As noted in the comments, .NET support for constraints is limited and only supports (i) paremeter-less constructors and (ii) implementation of an interface.
F# follows the same restructions for ordinary generic type parameters, but it also has statically resolved type parameters written as ^T, which can have more expressive constraints. Those are not compiled to .NET constraints, but instead, are erased during the compilation, so there are other constraints on those.
An example akin to what you have using static memnber constraints would look something like this:
type Foo<'T> =
{ Bar : 'T }
static member inline Demo< ^S when ^S : (static member Create : int -> ^S)>() =
{ Bar = (^S : (static member Create : int -> ^S) (10)) }
The constrains need to be on an inline member (or inline function), so I'm defining a generic type Foo<'T> which has a static member Demo that can be called with any type that has a Create method taking an int. For example:
type Sample(n:int) =
member x.N = n
static member Create(n:int) = Sample(n)
We can now call Foo.Demo with Sample as the type argument and we get Foo<Sample> back, created using 10 as the initialization value:
let f = Foo<_>.Demo<Sample>()
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)
Consider this interface:
type A<'a> =
abstract X : 'a
Let's try to implement it with int as a generic argument:
{ new A<int> with member this.X = 5 } // all is well
Now, let's try unit for an argument:
// Compiler error: The member 'get_X : unit -> unit' does not have the correct type to override the corresponding abstract method.
{ new A<unit> with member this.X = () }
Now, if we define a non-generic interface, everything also works well:
type A_int =
abstract X : int
{ new A_int with member this.X = 5 } // works
type A_unit =
abstract X : unit
{ new A_unit with member this.X = () } // works as well!
Is there anything I can do to fix this problem?
In F#, an abstract slot with declared return type of unit gets compiled in .NET IL as a return type of void. In contrast, an abstract slot with declared return type of "T" gets compiled in .NET IL as a generic return type of "T", which when T is instantiated by unit becomes unit'.
See : F# interface inheritance failure due to unit
Your generic member X can be a value of any type. 'unit' in F# is not really a type (or is very special type if you wish) - it's an absence of any value.
I have the below code:
type IQuery =
abstract List<'T> : unit -> IList<'T>
let create (str)=
let getList () : IList<'T> = upcast List<'T>()
{ new IQuery with
member this.List<'T>() = getList<'T>()
And for the last line it gives me a warning stating that:
The method or function 'getList' should not be given explicit type argument(s) because it does not declare its type parameters explicitly
However if I remove <'T> from getList call then I get a compilation error as :
The member 'List<'T> : unit -> IList<'a>' does not have the correct type to override the corresponding abstract method. The required signature is 'List<'T> : unit -> IList<'T>'.
What can I do ?
You can declare getList with an explicit type parameter:
let getList<'T> () : IList<'T> = upcast List<'T>()
You then get an error:
Explicit type parameters may only be used on module or member bindings
If you then move the let binding to the top-level at the same scope as the type, it all works:
type IQuery =
abstract List<'T> : unit -> IList<'T>
let getList<'T> () : IList<'T> = upcast List<'T>()
let create (str) =
{ new IQuery with
member this.List<'T>() = getList<'T>()
}
If your real code has getList using values only in scope in create, like str, you'll need to add them as explicit parameters to getList.
I have an type that is implementing IEnumerable<T> interface, all is ok:
open System
type Bar() =
interface Collections.IEnumerable with
member x.GetEnumerator () = null
interface Collections.Generic.IEnumerable<int> with
member x.GetEnumerator () = null
But things goes wrong if type inherits IEnumerable interface implementation via the base type:
open System
type Foo() =
interface Collections.IEnumerable with
member x.GetEnumerator () = null
type Bar() =
inherit Foo()
interface Collections.Generic.IEnumerable<int> with
member x.GetEnumerator () = null
Code above produces the type inference errors:
The member 'GetEnumerator<'a0 when 'a0 : null> : unit -> 'a0 when 'a0 : null' does not have the correct type to override any given virtual method
The member 'GetEnumerator<'a0 when 'a0 : null> : unit -> 'a0 when 'a0 : null' does not have the correct number of method type parameters. The required signature is 'GetEnumerator : unit -> Collections.Generic.IEnumerator<int>'.
Am I doing something wrong or this is an F# compiler bug?
Microsoft (R) F# 2.0 Interactive build 4.0.30319.1
Update more canonical example:
type IFoo = abstract Bar : obj list
type IFoo<'a> = abstract Bar : 'a list
inherit IFoo
/* ok */
type Foo = interface IFoo with member x.Bar = []
interface IFoo<Foo> with member x.Bar = []
/* fail */
type FooBase = interface IFoo with member x.Bar = []
type FooDerived = interface IFoo<Foo> with member x.Bar = [] // <---
inherit FooBase
/*
error FS0017: The member 'get_Bar : unit -> 'a list' does not
have the correct type to override any given virtual method.
*/
The compiler cannot infer the correct type from your "null"-implementation. Try
open System
type Foo() =
interface Collections.IEnumerable with
member x.GetEnumerator () = null
type Bar() =
inherit Foo()
interface Collections.Generic.IEnumerable<int> with
member x.GetEnumerator () : Collections.Generic.IEnumerator<int> = null
UPDATE:
The reason is, that the type of the GetEnumerator method implemented by the Bar type is ambigous as IEnumerable<'a> implements/inherits the non-generic IEnumerable which also specifies a (non-generic) GetEnumerator method. So, how should the compiler infer, which method exactly you are trying to implement if all he gets is null? Therefore we need a type annotation in this case.
This is not a bug, this is just an type inference fail because of F# may implement inherited interface members in the derived interface implementation declaration:
type IA = abstract A : int
type IB = inherit IA
type IC = inherit IB
type Baz =
interface IC with
member x.A = 1
So in my example I should specify the correct return type explicitly because member x.GetEnumerator() in derived Bar type may match both IEnumerable.GetEnumerator() and IEnumerable<T>.GetEnumerator().