【问题标题】:Error on Extension Methods when Inlining内联时扩展方法出错
【发布时间】:2016-06-28 09:01:51
【问题描述】:

我想扩展一些系统类型,然后通过内联使用它们

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

type System.Boolean with  
    member this.foo n = sprintf "%A!%A" this n 

现在我将这些扩展方法称为

let x = "foo".foo "bar"
let y = true.foo "bar"

这给了我这个

- val x : System.String = "foobar"
- val y : string = "true!"bar""

一切都很好——但现在我想将对 .foo 的调用封装到一个内联函数中

let inline foo n v = (^T : (member foo : ^N  -> ^S) v, n)
let z = foo "bar" "baz" 

直到现在我得到一个编译器错误告诉我

> The type 'string' does not support the operator 'foo':

嗯……确实如此!

有人能解释一下是怎么回事吗?

【问题讨论】:

    标签: f#


    【解决方案1】:

    静态成员约束中不考虑扩展方法(this 的可能重复项),当您想要使用成员约束实现泛型代码并使其也适用于已定义或原始类型时,这是一个普遍的问题。

    请参阅user voice 请求,以及提到的解决方法hereDon Syme's explanation of why it's complicated to implement it in the F# compiler

    如果您点击那里的链接,您将看到目前的解决方法,它基本上涉及为所有已知类型创建一个中间类型和重载,并为扩展创建一个通用类型。

    这是一个非常基本的解决方法的示例:

    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"
    

    您可以通过为新类型添加显式泛型重载来增强它:

    // 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"
    

    您可以通过手动编写静态约束并使用成员而不是运算符来进一步完善它(参见示例here),

    在一天结束时,您将得到类似来自 FsControl 的 generic append 函数。

    【讨论】:

      【解决方案2】:

      静态解析类型约束不支持扩展方法。这不是 F# 的功能。

      如果您希望 F# 获得对更高种类多态性的支持,您可以vote for it on user voice

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-10-04
        • 2017-06-01
        • 2010-09-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多