James Moore
James Moore

Reputation: 9026

Why are the signatures of these two methods different?

Why do Bind1 and Bind2 have different signatures?

type T() =
  let bind(v, f) = v
  member self.Bind1 = bind
  member self.Bind2(a, b) = bind(a, b)

fsi reports them as

type T =
  class
    new : unit -> T
    member Bind2 : a:'a * b:'b -> 'a
    member Bind1 : (obj * obj -> obj)
  end

This came up when I was playing with some computation expressions and couldn't figure out why I was getting an error message about Bind not being defined. Bind1-style didn't work, Bind2 did, and I couldn't figure out why.

Given the same objects, they do return the same result:

> q.Bind1(1:>obj,3:>obj);;
val it : obj = 1
> q.Bind2(1:>obj,3:>obj);;
val it : obj = 1
> 

Using Microsoft F# Interactive, (c) Microsoft Corporation, All Rights Reserved F# Version 1.9.7.4, compiling for .NET Framework Version v4.0.21006

Upvotes: 3

Views: 291

Answers (2)

gradbot
gradbot

Reputation: 13862

Bind1 is a get property that returns a function while bind2 is a function. You can see the get accessor if you evaluate bind1 and bind2 from an instance.

> let t = new T();;
val t : T
> t.Bind1;;
val it : (obj * obj -> obj) = <fun:get_Bind1@3>
> t.Bind2;;
val it : ('a * 'b -> 'a) = <fun:it@10>

You wrote the shorthand of

member self.Bind1
   with get() = bind

Using reflector you can see in Bind1 where obj comes from and the function object.

internal class get_Bind1@7 : FSharpFunc<Tuple<object, object>, object>
{
    // Fields
    public T self;

    // Methods
    internal get_Bind1@7(T self)
    {
        this.self = self;
    }

    public override object Invoke(Tuple<object, object> tupledArg)
    {
        object v = tupledArg.get_Item1();
        object f = tupledArg.get_Item2();
        return this.self.bind<object, object>(v, f);
    }
}

Along with what kvb said you can add type annotation to the class to avoid the generic objects.

type T<'a, 'b>() =
  let bind(v:'a, f:'b) = (v:'a)
  member self.Bind1 = bind
  member self.Bind2(a, b) = bind(a, b)

type T<'a,'b> =
  class
    new : unit -> T<'a,'b>
    member Bind2 : a:'a * b:'b -> 'a
    member Bind1 : ('a * 'b -> 'a)
  end

Upvotes: 7

kvb
kvb

Reputation: 55184

To elaborate on Erik's answer, because it is impossible to have generic properties on .NET objects, F# has to pick non-generic types for v and f, which default to obj. You could choose other specific types and use a type annotation to give Bind1 a different (but still non-generic) signature.

Upvotes: 4

Related Questions