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

Mar*_*nic 4 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.
Run Code Online (Sandbox Code Playgroud)

我想知道是否有办法让类型在上面的例子中做我想要的,或者我是否最终达到了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
Run Code Online (Sandbox Code Playgroud)

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

Gus*_*Gus 7

大多数现代类型系统的这种限制在kvb对这个问题的回答中得到了很好的解释.

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

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
Run Code Online (Sandbox Code Playgroud)

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

所以不要做,f x你会写f $ x.

UPDATE

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

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.
Run Code Online (Sandbox Code Playgroud)

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

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