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

前端 未结 2 953
孤街浪徒
孤街浪徒 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:27

    It will be something like

    ...Bind<'A when 'A :> 'Z>...
    

    but let me code it up to ensure that's exactly right...

    Ah, it looks like it would be this:

    type FinallyBuilder<'z> (finallyAction : 'z -> unit) = 
        member this.Bind<'a, 'b when 'a :> 'z> (x : 'a, cont : 'a -> 'b) : 'b = 
            try     cont x 
            finally finallyAction x //(x :> 'z)// illegal 
    

    except that

    http://cs.hubfs.net/forums/thread/10527.aspx

    points out that F# does not do contraints of the form "T1 :> T2" where both are type variables (it assumes T1 = T2). However this might be ok for your case, what exactly did you plan to use as concrete instantiations of Z? There is probably a simple workaround or some less-generic code that will meet the scenario. For example, I wonder if this works:

    type FinallyBuilder<'z> (finallyAction : 'z -> unit) = 
        member this.Bind<'b> (x : 'z, cont : 'z -> 'b) : 'b = //'
            try     cont x 
            finally finallyAction x 
    

    It seems to:

    type FinallyBuilder<'z> (finallyAction : 'z -> unit) = 
        member this.Bind<'b> (x : 'z, cont : 'z -> 'b) : 'b = // '
            try     cont x 
            finally finallyAction x 
        member this.Zero() = ()
    
    []
    type Animal() =
        abstract Speak : unit -> unit
    
    let cleanup = FinallyBuilder (fun (a:Animal) -> a.Speak())
    
    type Dog() =
        inherit Animal()
        override this.Speak() = printfn "woof"
    
    type Cat() =
        inherit Animal()
        override this.Speak() = printfn "meow"
    
    cleanup {
        let! d = new Dog()
        let! c = new Cat()
        printfn "done"
    }
    // prints done meow woof
    

    Oh, I see, but d and c now have type Animal. Hm, let me see if there is any remaining cleverness in me...

    Well, obviously you can do

    type FinallyBuilder<'z> (finallyAction : 'z -> unit) = 
        member this.Bind<'a,'b> (x : 'a, cont : 'a -> 'b) : 'b = // '
            try     cont x 
            finally finallyAction (x |> box |> unbox)
        member this.Zero() = ()
    

    which throws away type safety (will throw a cast exception at runtime if the thing is not finallyActionable).

    Or you can make type-specific builders:

    type FinallyBuilderAnimal (finallyAction : Animal -> unit) = 
        member this.Bind<'a,'b when 'a:>Animal>(x : 'a, cont : 'a -> 'b) : 'b = //'
            try     cont x 
            finally finallyAction x
        member this.Zero() = ()
    
    let cleanup = FinallyBuilderAnimal (fun a -> a.Speak())
    

    But I think I am out of other clever ideas.

提交回复
热议问题