扩展方法和鸭子类型
Extension Methods and 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")
效果很好
不幸的是,扩展成员不能从成员约束中使用。这可以在编译器中实现,但我怀疑它会很快实现——据我所知,成员约束是 F# 团队的低优先级功能。
编辑:对于您自己的类型:发生的情况是,当您在与类型本身相同的模块中定义类型扩展时,它会被编译为您的类型的普通方法。如果将扩展移动到另一个模块,那么它会真正编译为扩展,您将看到与 System.String
.
相同的行为
我不太确定 Gustavo 提到的替代方法是否适用于您的原始代码,因为 String 扩展方法与您的其他 transform
方法具有不同的签名,member transform : x:string -> Vehicle<System.String>
与member transform : x:'a -> Vehicle<'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)
为什么尝试在字符串上调用 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# 团队的低优先级功能。
编辑:对于您自己的类型:发生的情况是,当您在与类型本身相同的模块中定义类型扩展时,它会被编译为您的类型的普通方法。如果将扩展移动到另一个模块,那么它会真正编译为扩展,您将看到与 System.String
.
我不太确定 Gustavo 提到的替代方法是否适用于您的原始代码,因为 String 扩展方法与您的其他 transform
方法具有不同的签名,member transform : x:string -> Vehicle<System.String>
与member transform : x:'a -> Vehicle<'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)