Error on Extension Methods when Inlining

后端 未结 2 675
北荒
北荒 2021-01-13 14:57

I want to extend some system types and later use them via inlining

type System.String with  
    member this.foo n = this + \"!\" + n 

type System.Boolean w         


        
相关标签:
2条回答
  • 2021-01-13 15:17

    Statically resolved type constraints do not support extension methods. It's just not a feature of F#.

    If you would like F# to gain support for higher-kinded polymorphism, you can vote for it on user voice.

    0 讨论(0)
  • 2021-01-13 15:19

    Extension methods are not taken into account in static member constraints (possible duplicate of this) and this is a general problem when you want to implement generic code using member constraints and make it work also with already defined or primitive types.

    See the user voice request, also the workarounds mentioned here and Don Syme's explanation of why it's complicated to implement it in the F# compiler.

    If you follow the links there you will see currently the way to workaround it basically involves creating an intermediate type and overloads for all the known types and a generic one for the extensions.

    This is a very basic example of how to workaround it:

    type Foo = Foo with
        static member ($) (Foo, this:int)    = fun (n:int) -> this + n 
        static member ($) (Foo, this:string) = fun n -> this + "!" + n 
        static member ($) (Foo, this:bool)   = fun n -> sprintf "%A!%A" this n 
    
    let inline foo this n = (Foo $ this) n
    
    //Now you can create your own types with its implementation of ($) Foo.
    
    type MyType() =
        static member ($) (Foo, this) = 
            fun n -> printfn "You called foo on MyType with n = %A" n; MyType()
    
    let x = foo "hello" "world"
    let y = foo true "world"
    let z = foo (MyType()) "world"
    

    You can enhance it by adding an explicit generic overload for new types:

    // define the extensions
    
    type System.String with  
        member this.foo n = this + "!" + n 
    
    type System.Boolean with  
        member this.foo n = sprintf "%A!%A" this n 
    
    // Once finished with the extensions put them in a class
    // where the first overload should be the generic version.
    type Foo = Foo with
        static member inline ($) (Foo, this) = fun n -> (^T : (member foo : ^N -> ^S) this, n)
        static member ($) (Foo, this:string) = fun n -> this.foo n 
        static member ($) (Foo, this:bool)   = fun n -> this.foo n
        // Add other overloads
        static member ($) (Foo, this:int)    = fun n -> this + n 
    
    let inline foo this n = (Foo $ this) n
    
    //later you can define any type with foo
    type MyType() =
        member this.foo n = printfn "You called foo on MyType with n = %A" n; MyType()
    
    // and everything will work
    let x = foo "hello" "world"
    let y = foo true "world"
    let z = foo (MyType()) "world"
    

    You can further refine it by writing the static constraints by hand and using a member instead of an operator (see an example here),

    At the end of the day you will end up with something like this generic append function from FsControl.

    0 讨论(0)
提交回复
热议问题