Statically Resolved Type Parameters

前端 未结 2 733
一整个雨季
一整个雨季 2020-12-10 18:27

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

type A< ^B          


        
相关标签:
2条回答
  • 2020-12-10 18:47

    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.

    0 讨论(0)
  • 2020-12-10 18:57

    Your implementation @Tomas satisfies the question but there is some hesitation over the style of your solution because it does not respected the functional programming paradigm. I thought to a solution arising from the Type Classes implementation of Haskell. Interfaces, abstract classes, etc. have been implemented in order to allow the F# environment to “interact” with the .Net environment, for a reason of uniformity of style the use of code which implements interfaces, abstract classes etc. in a context where no need of interact with the .Net libraries, it is a “Overhead” in my opinion (although F# is a multi-paradigm language). This is the reason why I found the implementation of the Type Classes of Haskell very elegant. Below I implemented through F# the “Haskell Type Class” code to solve my problem.

    type Operations< ^a> = 
      {  
        MyMember : unit -> unit
      }
    
    type A< ^a> = {
      Operations : Operations< ^a>
    }
    
    and TestA = { 
      AField : A< BTy > 
    }
    
    and BTy = { 
      BField : Unit 
    }
    
    let it = 
      let BTy_operations : Operations< BTy > = { MyMember = fun () -> () }
      let A_of_BTy = { Operations = BTy_operations }
      { AField = A_of_BTy }
    
    0 讨论(0)
提交回复
热议问题