【问题标题】:Extension Methods and Duck Typing扩展方法和鸭子类型
【发布时间】:2015-02-23 14:30:04
【问题描述】:

为什么我在尝试对字符串调用 transform 时会出错?

type Truck = Truck 
type Car = Car

type Vehicle<'a> =
    | TruckWrapper of Truck * 'a
    | CarWrapper of Car * 'a 

type Truck with
    member this.transform (x) = TruckWrapper(this, x)

type Car with
     member this.transform (x) = CarWrapper(this, x)

type System.String with 
    member this.transform (x) =
        match x with
        | "truck" -> TruckWrapper(Truck, this)
        | _ -> CarWrapper(Car, this)

let inline transform value x = (^T : (member transform : 'a -> Vehicle<'b>) (value, x))

let a = transform Truck 1
let b = transform Car (1, 2)
let c = transform "truck" 0

这将产生以下错误

let c = transform "truck" 0
------------------^^^^^^^

stdin(77,19): error FS0001: The type 'string' does not support the operator 'transform'

let d = "vehicle".transform("truck") 

效果很好

【问题讨论】:

    标签: f# extension-methods duck-typing


    【解决方案1】:

    不幸的是,扩展成员不能从成员约束中使用。这可以在编译器中实现,但我怀疑它会很快实现——据我所知,成员约束对于 F# 团队来说是一个低优先级的特性。

    编辑:对于您自己的类型:发生的情况是,当您在与类型本身相同的模块中定义类型扩展时,它会被编译为您的类型的普通方法。如果您将扩展移动到另一个模块,那么它会真正编译为扩展,您将看到与 System.String 相同的行为。

    【讨论】:

    • 嗯,它们是 - 至少对于我自己的类型而言。
    • 开枪!现在真是令人沮丧。谢谢你的回答
    • 我认为重要的是要注意,有一种方法可以通过创建中间类型和类似运算符的方法调用来解决此限制。
    • 嗨 Gustavo,你能详细解释一下你将如何解决这个问题吗?
    【解决方案2】:

    我不太确定 Gustavo 提到的替代方法是否适用于您的原始代码,因为字符串扩展方法与您的其他 transform 方法具有不同的签名,member transform : x:string -&gt; Vehicle&lt;System.String&gt;member transform : x:'a -&gt; Vehicle&lt;'a>` 否则.

    如果它们都具有相同的类型,那么它类似于:

    type IntermediateVehicle = IntermediateVehicle with
        static member ($) (IntermediateVehicle, x : Truck) =
            fun value -> x.transform value
        static member ($) (IntermediateVehicle, x : Car) =
            fun value ->  x.transform value
        static member ($) (IntermediateVehicle, x : string) =
            fun value -> x.transform value
    
    let inline transform value x = (IntermediateVehicle $ value) x
    
    let a = transform Truck 1
    let b = transform Car (1, 2)
    let c = transform "truck" 0
    // val a : Vehicle<int> = TruckWrapper (Truck,1)
    // val b : Vehicle<int * int> = CarWrapper (Car,(1, 2))
    // val c : Vehicle<int> = TruckWrapper (Truck,0)
    

    【讨论】:

    • 太棒了!感谢这个例子。下一个问题已经迫在眉睫。似乎这整件事只适用于中缀运算符。例如,如果我将static member toVehicle (IntermediateVehicle, x : Truck) = x.transform() 和内联函数定义为let inline transform value = IntermediateVehicle.toVehicle(value),则会出现错误。为什么? (我只是省略了第二个参数)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-21
    相关资源
    最近更新 更多