How do I translate a `where T : U` generic type parameter constraint from C# to F#?

前端 未结 2 950
孤街浪徒
孤街浪徒 2020-12-15 21:16

F# is giving me some trouble with its type inference rules. I\'m writing a simple computation builder but can\'t get my generic type variable constraints right.


<
2条回答
  •  温柔的废话
    2020-12-15 21:26

    I don't think it is possible to write constraint like this in F# (although I'm not exactly sure why). Anyway, syntacticalaly, you'd want to write something like this (as Brian suggests):

    type FinallyBuilder<'T> (finallyAction : 'T -> unit) = 
      member this.Bind<'A, 'B when 'A :> 'T>(x : 'A) (cont : 'A -> 'B) =  //' 
        try cont x 
        finally finallyAction (x :> 'T) 
    

    Unfortunatelly, this gives the following error:

    error FS0698: Invalid constraint: the type used for the constraint is sealed, which means the constraint could only be satisfied by at most one solution

    This seems to be the same case as the one discussed in this mailing list. Where Don Syme says the following:

    This is a restriction imposed to make F# type inference tractable. In particular, the type on the right of a subtype constraint must be nominal. Note constraints of the form 'A :> 'B are always eagerly solved to 'A = 'B, as specified in section 14.5.2 (Solving Subtype Constraints) of the F# specification.

    You can always solve this by using obj in the function passed to your builder.
    EDIT: Even when you use obj, the values bound using let! will have more specific types (when calling finallyAction, F# will automatically cast the value of some type parameter to obj):

    type FinallyBuilder(finallyAction : obj -> unit) =  
      member x.Bind(v, f) =  
        try f v 
        finally finallyAction v 
      member x.Return(v) = v
    
    let cleanup = FinallyBuilder(printfn "%A")
    
    let res = 
      cleanup { let! a = new System.Random()
                let! b = "hello"
                return 3 }
    

提交回复
热议问题