是否可以使以下示例在F#中完全多态? [英] Is it possible to make the following example fully polymorphic in F#?

查看:80
本文介绍了是否可以使以下示例在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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆