Is this an F# bug? - f#

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

Related

Bind variable to interface type in FSI

I'm experimenting with integration of F# as scripting language and have an issue with FsiEvaluationSession.AddBoundVariable method. Problem is that this method creates variable of actual type of object, but I need to create variable of interface that it implements. I can't find AddBoundVariable<T>(string, T) or any other overload that would allow be do that.
// located in common assembly
type IFoo =
abstract Foo : unit -> unit
type FooImpl() =
interface IFoo with
member _.Foo () = ()
// located in host
session.AddBoundVariable ("foo", Foo())
session.EvalInteraction "foo.Foo()" // throws, `FooImpl` type doesn't have `Foo` method
session.EvalInteraction """
let foo : IFoo = foo
foo.Foo()
""" // throws, `IFoo` not found
Question is: how can I create variable of type that I want?
You have to explicitly cast the Foo instance to IFoo, so this should work:
session.EvalInteraction """
let foo = foo :> IFoo
foo.Foo()
"""
To avoid the indirection of FSI, you can try this in your compiled code by simply binding foo as normal first:
let foo = FooImpl()
let foo : IFoo = foo // ERROR: This expression was expected to have type 'IFoo' but here has type 'FooImpl'
let foo = foo :> IFoo // works fine
foo.Foo()

F# constructor constraint

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>()

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.

Why is `unit` treated differently by the F# type system when used as a generic interface argument?

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.

F# generics for inner functions

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.

Resources