扩展方法和鸭子打字

时间:2015-02-23 14:30:04

标签: f# extension-methods duck-typing

为什么在尝试在字符串上调用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") 

工作得很好

2 个答案:

答案 0 :(得分:3)

不幸的是,扩展成员不能从成员约束中使用。这可以在编译器中实现,但我怀疑它会很快发生 - 据我所知,成员约束是F#团队的低优先级功能。

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

答案 1 :(得分:2)

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

如果它们的类型相同,那么它就是:

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)