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
但假设第一个例子有效,那将是一个更动态的解决方案。遗憾的是,在一个参数中通过名称传递一个更高阶的函数,并在现场内联它以使其成为通用是不可能的。
答案 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 $
意味着函数应用。