F#类型参数缺少约束

时间:2011-07-27 18:46:13

标签: generics f# type-constraints type-parameter

我正在尝试为包装类定义一个通用的加法运算符。到目前为止我有这个:(从实际代码简化)

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'不起作用?谢谢!

4 个答案:

答案 0 :(得分:4)

正如我在评论中所说,我认为这是一个错误。这是我的推理。当编译器看到

let inline addSelf (Wrap x) = (Wrap x) + (Wrap x)

我认为它应该大致做出以下推论:

  1. 对于一些新鲜的Wrap< ^t>,参数的类型为^t
  2. 因此x的类型为^t
  3. 因此,右侧的操作数也都是Wrap< ^t>类型。
  4. 这些值将传递到(+)运算符。因此,对于某些新鲜类型Wrap< ^t>(+)需要支持Wrap< ^t> * Wrap< ^t> -> ^u类型的静态运算符^u
  5. (+)上定义的唯一静态运算符Wrap<_>的类型为Wrap< ^a> * Wrap< ^b> -> Wrap< ^c> when (^a or ^b) : (static member (+) : ^a * ^b -> ^c
  6. 统一类型变量,addSelf的总体类型应为addSelf : Wrap< ^t> -> Wrap< ^c> when ^t : (static member (+) : ^t * ^t -> ^c)
  7. 各种类型的推理步骤都很棘手,因此我肯定可能会遗漏某些内容并且这种行为是预期的。另一方面,各种类型的推理步骤是棘手的,所以他们有点儿马车:)。还有一点是你无法注释函数和所有子表达式并获得编译代码:

    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类型上找到+运算符,并且+已经内联,它与搜索条件不匹配。