我正在尝试为包装类定义一个通用的加法运算符。到目前为止我有这个:(从实际代码简化)
type Wrap<'a> =
| Wrap of 'a
static member inline (+) (Wrap x, Wrap y) = Wrap (x + y)
let inline addSelf x = x + x
确实有效:
let i = addSelf (Wrap 1) // returns Wrap 2
let f = addSelf (Wrap 1.) // returns Wrap 2.0
但addSelf的以下替代方法无法编译
let inline addSelf' (Wrap x) = (Wrap x) + (Wrap x) // compile error
给出错误FS0193:类型参数缺少约束'when(^ a或^?15169):(静态成员(+):^ a * ^?15169 - &gt; ^?15170)'
当addSelf工作正常时,为什么更受限制的addSelf'不起作用?谢谢!
答案 0 :(得分:4)
正如我在评论中所说,我认为这是一个错误。这是我的推理。当编译器看到
时let inline addSelf (Wrap x) = (Wrap x) + (Wrap x)
我认为它应该大致做出以下推论:
Wrap< ^t>
,参数的类型为^t
。x
的类型为^t
。Wrap< ^t>
类型。(+)
运算符。因此,对于某些新鲜类型Wrap< ^t>
,(+)
需要支持Wrap< ^t> * Wrap< ^t> -> ^u
类型的静态运算符^u
。(+)
上定义的唯一静态运算符Wrap<_>
的类型为Wrap< ^a> * Wrap< ^b> -> Wrap< ^c> when (^a or ^b) : (static member (+) : ^a * ^b -> ^c
。addSelf
的总体类型应为addSelf : Wrap< ^t> -> Wrap< ^c> when ^t : (static member (+) : ^t * ^t -> ^c)
各种类型的推理步骤都很棘手,因此我肯定可能会遗漏某些内容并且这种行为是预期的。另一方面,各种类型的推理步骤是棘手的,所以他们有点儿马车:)。还有一点是你无法注释函数和所有子表达式并获得编译代码:
let inline doStuff< ^t, ^u when ^t : (static member (+) : ^t * ^t -> ^u)> ((Wrap x) : Wrap< ^t>) : Wrap< ^u> =
((Wrap x) : Wrap< ^t>) + ((Wrap x) : Wrap< ^t>)
您仍然会遇到编译错误,其中包含对新鲜类型参数^?12020
和^?12021
的神秘引用(或者您的案例中的任何唯一整数)。我认为这不应该发生。
答案 1 :(得分:3)
如果你看一下FSI中Wrap的定义,你会发现静态(+)运算符只是为支持限制的类型定义的,而Wrap本身是为任何类型定义的。
当编写第一个版本时,类型推断会为您添加该限制...但您的第二个版本允许x的任何类型(即使对于不支持静态(+)运算符的x)。
即。第二个版本表明可以编写一个支持任何类型的Wrap&lt; T&gt;的函数。虽然不能保证'T支持+运算符。
编辑: 正如Tomas指出的那样,这是可能的,但你必须使用一种特殊的语法来定义要调用的成员,以下是有效的:
let inline addSelf (x : Wrap<_>) =
((^a or ^b): (static member (+) : ^a * ^b -> ^c) (x,x))
答案 2 :(得分:0)
我认为addSelf'
应该仍为
let inline addSelf' x = x + x
但是使用正确的类型注释:
let inline addSelf' (x : Wrap) = x + x
或类似的东西。我对F#有点生气,现在无法测试它。
答案 3 :(得分:0)
如果从静态(+)函数中删除内联,即
type Wrap<'a> =
| Wrap of 'a
static member (+) (Wrap x, Wrap y) = Wrap (x + y)
比它运作正常。
我想这是因为在addSelf'
中,编译器尝试在Wrap类型上找到+运算符,并且+已经内联,它与搜索条件不匹配。