Interfaces Explained

Can someone explain this F # curiosity?

type IFoo = abstract member Bar1: int * int -> int * int abstract member Bar2: int * int -> (int * int) abstract member Bar3: (int * int) -> int * int type Foo() = class end with interface IFoo with member this.Bar1 (x, y) = (x, y) member this.Bar2 (x, y) = (x, y) // Same impl as Bar1 ie parentheses on RHS of -> in interface member definition are ignored // member this.Bar3 (x, y) = (x, y) // Compile error: "This override takes a different number of arguments to the corresponding abstract member" member this.Bar3 tuple = tuple // So, parentheses on LHS of -> in interface member definition *does* make a difference! 

What is the difference in value between the definitions of IFoo.Bar1 and IFoo.Bar3 ?

+8
f #
source share
1 answer

Here, the input type can describe two different things: a tuple or an argument list of the CLI method.

This has nothing to do with the return type, since the only interpretation of this return type is a tuple. But in the argument list, you can choose between a CLI method that takes two arguments, or a CLI method that takes one argument, which is a tuple. The latter are indicated by additional brackets.

That's why you can implement Bar3 with one argument typed as a tuple, which is not possible with others (with F # 3).

This is also the place where double parentheses matter for single parentheses. If Bar3 were not abstract, you could provide tuple input by declaring it as member this.Bar3 ((arg1, arg2)) .

The argument list of the method contains additional functions, such as optional arguments. So:

 type Test () = member t.BarA(a, ?b) = a member t.BarT((a, ?b)) = a // Error 

The last line gives the error "Optional arguments are allowed only for type members", since b now part of the tuple template, and not an argument in the method argument list.

+9
source share

All Articles