是否可以使以下示例在F#中完全多态? [英] Is it possible to make the following example fully polymorphic in 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类型进行模式匹配,如下所示:
I am wondering if there is a way to make the types do what I want in the example above or if I have finally reached the limitation of F#'s type system. Actually, there is a way to make the above work and that is to put all the types into one DU and then pattern match on the DU type like the following:
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
但是假设第一个示例可行,它将是一个更具动态性的解决方案.遗憾的是,像在参数内传递名称的高阶函数,然后当场内联使其通用,就不可能了.
But assuming the first example worked, it would be a more dynamic solution. It is a pity something like passing a higher order function by name inside an argument and having it inlined on the spot to make it generic is not possible.
推荐答案
kvb对这是一个变通方法,根据那里建议的黑客技巧进行.实际上,它与您的代码非常相似,但是比较冗长.
Here's a workaround, based on the hack suggested there. In fact it's very similar to your code but less verbose.
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
这样的想法是,在这种情况下,它不是使用单个方法(您已经做过)定义类型的方法(您已经做过),而是一个运算符,意味着应用.
The idea is that instead of defining a function you define a type with a single method (which you already did) in this case it will be an operator which would mean apply.
因此,您无需编写f x
,而是编写f $ x
.
So instead of doing f x
you will write f $ x
.
更新
如前所述,您的代码与该答案中建议的解决方案相距不远. 这是一个更接近原始代码的工作示例:
As said before, your code is not far from the solution suggested in that answer. Here's a working example which is even closer to your original code:
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
.通过这样做,编译器不会及早解决约束,因此它可以工作.
So that's basically your original code but using static methods and using an or
in the constraints. By doing this the compiler doesn't solve the constraint early and so it works.
我使用运算符是因为它不太冗长,在这种情况下,我喜欢它的读取方式,因为Haskell $
表示函数应用程序.
I used the operator because it's less verbose and in this case I like how it reads, since Haskell $
means function application.
这篇关于是否可以使以下示例在F#中完全多态?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!