views:

116

answers:

2

I know I must be missing something really obvious here. B.GetInstance().Call() generates the 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'm using v1.9.9.9.

type A() =
    member x.Call() = B.GetInstance().Call()

and B() =
    static member GetInstance() = new B()
    member x.Call() = ()

I just discovered that this works: (B.GetInstance() :> B).Call()

Any idea why the cast is necessary?

+5  A: 

Frequently when you've got a recursive set of methods whose types to infer, F# needs help. A more pleasant alternative would be to annotate the definition of B.GetInstance:

type A() =
  member x.Call() = B.GetInstance().Call()

and B() =
  static member GetInstance() : B = new B()
  member x.Call() = ()

I believe that the reason you run into this problem is that F# tries to solve all inferred types on all methods in A and B simultaneously (because they are defined as mutually recursive types), and this leads to problems, but perhaps someone from the F# team will weigh in.

kvb
+5  A: 

The quick summary is that in a recursive group (e.g. members in one type, or members of recursive types like we have here) F# reads the declarations in left-to-right top-to-bottom order, followed by the definitions in left-to-right top-to-bottom order. So in this instance when it reaches the definition of A.Call, it has not yet read the definition of B.GetInstance and therefore does not (yet!) know that the return type of GetInstance will be B.

Keith's answer nails it for this situation, you can provide a type annotation to specify the return type of GetInstance in its declaration.

See

http://stackoverflow.com/questions/1809405/forcing-f-type-inference-on-generics-and-interfaces-to-stay-loose/1809604#1809604

for a deep discussion of what's going on here.

Note also that in your original attempt, you don't need to "cast" (the potentially dynamic operation, using :>), instead you can just "annotate" (statically declare a type, using :) to get it to compile. But makes more sense to put the type annotation in the method declaration for GetInstance (generally, prefer addition annotations to method signatures instead of arbitrary places inside bodies).

Brian
Great explanation. If I could mark two answers I would.
Daniel