Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Statically Resolved Type Parameters

The following (simplified) snippet is taken from an application I'm implementing which consistently uses Type Parameters resolved statically.

type A< ^B when ^B : (static member MyMember : Unit -> Unit)> = {
  Field : unit
}
type TestA = {
  AField : A< BTy >
}
and BTy = {
  BField : Unit
} with
  static member MyMember () = ()

IntelliSense gives me the following error when I define the type of field AField (AField : A< BTy >) which is: The type 'BTy' does not support any operators named 'MyMember'.

EDITED. Declaring them separately works, but if I have a mutual reference and I cannot declare a third type to put in the top which contains the common Infos of the two types. What should I do to avoid this problem? Anyway, if I define below the definitions let pluto = ("" :> obj) :?> A< BTy > it works, I imagine because both types are visible from the let binding.

like image 474
Mohamed Abbadi Avatar asked Oct 14 '12 11:10

Mohamed Abbadi


1 Answers

To be honest, I'm a bit surprised that you are even allowed to use static member constraints in a type declaration, but as mentioned by @pad, when you put the declarations in the right order and remove the recursion, it works (although I'm not sure that there won't be other limitations when you move to more complex examples):

type A< ^B when ^B : (static member MyMember : Unit -> Unit)> = 
  { Field : unit } 

type BTy = 
  { BField : Unit } 
  static member MyMember () = () 

type TestA = { AField : A<BTy> }

Anyway, I think using static member constraints in a type declaration is a bit complicated. A cleaner way to do this would be to define an interface that clearly describes (and documents) the members you need:

type IMyMember =
  abstract MyMember : unit -> unit

Now, static member constraints can still be used to create an implementation of the interface from a type that has the required member, but does not implement the interface. Using this technique, you should be able to implement exactly the same functionality as with static member constraints on types (but in a clearer way):

/// Captures 'IMyMember' implementation from another type using static constraints
let inline captureMyMember< ^B when ^B : (static member MyMember : Unit -> Unit)> =
  { new IMyMember with
      member x.MyMember () = 
        (^B : (static member MyMember : Unit -> Unit) ()) }

The function will, for example, create IMyMember from your BTy type:

/// A type that contains field and a captured implementation of 'IMyMember'
type A = 
  { Field : unit 
    Operations : IMyMember } 

let it = { Field = ()
           Operations = captureMyMember<BTy> }

Aside, I used the same technique in an article that shows how to write generic numeric code and I think that it worked there really nicely.

like image 153
Tomas Petricek Avatar answered Oct 23 '22 23:10

Tomas Petricek