是否有可能在F#中使以下示例完全多态?

时间:2016-09-01 13:59:29

标签: f#

type Mul = Mul with member inline __.Op(a: ^a,b: ^a) = a*b
type Div = Div with member inline __.Op(a: ^a,b: ^a) = a/b
type Add = Add with member inline __.Op(a: ^a,b: ^a) = a+b
type Sub = Sub with member inline __.Op(a: ^a,b: ^a) = a-b

let inline op x a b =
    (^a: (member Op: ^b * ^b -> ^b) x,a,b)

let inline tup2 a b c d = op Mul a b, op Mul c d
let inline tup2' f a b c d = op f a b, op f c d

let a = tup2 1 2 3.0f 4.0f
//let b = tup2' Mul 1 2 3.0f 4.0f //Gives a type error.

我想知道是否有办法让类型在上面的例子中做我想做的事情,或者我是否最终达到了F#的类型系统的限制。实际上,有一种方法可以完成上述工作,即将所有类型放入一个DU中,然后在DU类型上进行模式匹配,如下所示:

type Operation = 
    | Mul 
    | Add
    | Sub
    | Div
    member inline t.Op a b = 
        match t with 
        | Mul -> a * b
        | Add -> a + b
        | Sub -> a - b
        | Div -> a / b
let inline map' (f: Operation) a b c d =
    (f.Op a b, f.Op c d)
map' Mul 1 2 3.0f 4.0f

但假设第一个例子有效,那将是一个更动态的解决方案。遗憾的是,在一个参数中通过名称传递一个更高阶的函数,并在现场内联它以使其成为通用是不可能的。

1 个答案:

答案 0 :(得分:7)

kvb对this question的回答很好地解释了大多数现代类型系统的这种限制。

这是一个解决方法,基于那里建议的黑客。事实上,它与你的代码非常相似,但不那么冗长。

type Mul = Mul with static member inline ($) (Mul, a: ^a) = fun (b: ^a) -> a*b
type Div = Div with static member inline ($) (Div, a: ^a) = fun (b: ^a) -> a/b
type Add = Add with static member inline ($) (Add, a: ^a) = fun (b: ^a) -> a+b
type Sub = Sub with static member inline ($) (Sub, a: ^a) = fun (b: ^a) -> a-b

let inline tup2' f a b c d = (f $ a) b, (f $ c) d

let b = tup2' Mul 1 2 3.0f 4.0f

我们的想法是,不是定义一个函数,而是用一个方法(你已经做过)定义一个类型,在这种情况下,它将是一个意味着应用的运算符。

所以,不要执行f x,而是编写f $ x

<强>更新

如前所述,您的代码与该答案中建议的解决方案相差不远。 这是一个更接近原始代码的工作示例:

type Mul = Mul with static member inline Op(Mul, a: ^a,b: ^a) = a*b
type Div = Div with static member inline Op(Div, a: ^a,b: ^a) = a/b
type Add = Add with static member inline Op(Add, a: ^a,b: ^a) = a+b
type Sub = Sub with static member inline Op(Sub, a: ^a,b: ^a) = a-b

let inline op x a b = ((^a or ^b): (static member Op: ^a * ^b  * ^b -> ^b) (x, a, b))

let inline tup2 a b c d = op Mul a b, op Mul c d
let inline tup2' f a b c d = op f a b, op f c d

let a = tup2 1 2 3.0f 4.0f
let b = tup2' Mul 1 2 3.0f 4.0f //Gives NO type error.

因此,这基本上是您的原始代码,但使用静态方法并在约束中使用or。通过这样做,编译器不会提前解决约束,因此它可以正常工作。

我使用了运算符,因为它不那么冗长,在这种情况下我喜欢它的读取方式,因为Haskell $意味着函数应用。