【问题标题】:Static member constraint for op_additionop_addition 的静态成员约束
【发布时间】:2016-08-02 09:42:42
【问题描述】:

为了学习,我正在尝试使用 pointfree inline 函数重新发明 List.foldList.reduceList.sum。这是我所拥有的:

let flip f y x = f x y

let rec fold folder seed = function
    | [] -> seed
    | h :: t -> fold folder (folder seed h) t

let inline reduce< ^a when ^a : (static member Zero : ^a) > : 
    (^a -> ^a -> ^a) -> ^a list -> ^a =
    flip fold LanguagePrimitives.GenericZero

let inline sum< ^a when 
            ^a : (static member (+) : ^a * ^a -> ^a) and 
            ^a : (static member Zero : ^a)> : ^a list -> ^a = 
    reduce (+)
           // ^ Compile error here

我知道这种冗长的类型约束在 F# 中并不常用,但我正在努力学习,所以请多多包涵。

我遇到了一个非常神秘的编译错误,我不明白:

Type mismatch. Expecting a
    'a -> 'a -> 'a 
but given a
    'a -> 'b -> 'c     
A type parameter is missing a constraint 'when ( ^a or  ^?269978) : (static member ( + ) :  ^a *  ^?269978 ->  ^?269979)' 
A type parameter is missing a constraint 'when ( ^a or  ^?269978) : (static member ( + ) :  ^a *  ^?269978 ->  ^?269979)' 
val ( + ) : x:'T1 -> y:'T2 -> 'T3 (requires member ( + )) 
Full name: Microsoft.FSharp.Core.Operators.( + ) 
Overloaded addition operator
x: The first parameter. 
y: The second parameter.

我应该添加什么来满足编译器?

【问题讨论】:

  • 我似乎无法重现该问题,因为即使reduce 也无法为我编译。我有预期的'a -&gt; 'b -&gt; 'a -&gt; 'a 但给定('c -&gt; 'd -&gt; 'c) -&gt; 'c -&gt; 'd list -&gt; 'c 类型'a 与指向折叠 的类型'b -&gt; 'c -&gt; 'b 不匹配
  • 顺便说一句,List.reduce 的 F# 实现在空列表上失败,并且不需要 GenericZero。例如,你可以这样定义reduce: let rec reduce op = function | [] -> 以“空”失败 | [x] -> x | h :: t -> op h(减少 op t)
  • @Sehnsucht 抱歉,这是我的错字,我添加了类型注释来修复 reduce 处的编译错误

标签: f#


【解决方案1】:

我不知道完全按照您的意愿行事的方法,因为我不知道以无点样式调用静态约束的静态成员。请注意,您没有调用^a(+) 并且编译器不知道如何解决这个问题...如果您对使用内部辅助函数感到满意,您可以这样做:

let inline sum< ^a
    when ^a : (static member (+) : ^a -> ^a -> ^a) and
         ^a : (static member Zero : ^a) > : ^a list -> ^a =
    let add x y = (^a : (static member (+) : ^a -> ^a -> ^a) (x, y))
    reduce add

注意警告

警告 FS0077:名称为“op_Addition”的成员约束被 F# 编译器赋予特殊状态,因为某些 .NET 类型隐式地使用此成员进行扩充。如果您尝试从自己的代码中调用成员约束,这可能会导致运行时失败。

确实:

sum [1; 2; 3] 导致

System.NotSupportedException:不支持指定的方法。
在 FSI_0005.it@15-1.Invoke(Int32 x, Int32 y) in ...

但是如果你把sum改成:

let inline sum list =
    reduce (+) list

sum [1; 2; 3] // 6

【讨论】:

    【解决方案2】:

    我无益的回答是:不要那样做。内联定义仅用作语法函数,因此您不应该以无点的方式定义它们。例如,比较尝试评估以下三行的结果:

    let inline identity = id
    let inline identity : ^a -> ^a = id
    let inline identity< ^a> : ^a -> ^a = id
    

    只有最后一个编译,(从技术上讲,这是因为它是一个“类型函数”,它基本上是一个在幕后传递一个不可见的 unit 值以允许它被视为通用值的函数)。

    但是,如果您坚持以这种方式做事,那么“修复”它的一种方法是使 (+) 不那么通用;默认情况下,参数和返回类型可以是不同的类型,这种灵活性引入的约束是让编译器感到困惑的部分原因。这是一个修复:

    let inline reduce< ^a, 'b  when  ^a : (static member Zero : ^a)> : ( ^a -> 'b ->  ^a) -> ('b list ->  ^a) =
        flip fold LanguagePrimitives.GenericZero
    
    let inline (+) (x:^a) (y:^a) = x + y
    
    let inline sum< ^a
                     when ^a : (static member ( + ) :  ^a * ^a -> ^a)
                      and ^a : (static member Zero : ^a)> : ^a list -> ^a =
        reduce (+)
    

    【讨论】:

      【解决方案3】:

      像 Vandroiy 一样,给定的 reduce 函数不能为我编译。我认为你想要的 reduce 函数实际上是:

      let inline reduce< ^a when ^a : (static member Zero : ^a) > : ((^a -> ^a -> ^a) -> ^a list -> ^a)  =
          flip fold LanguagePrimitives.GenericZero
      

      这样做我能够复制您给定的错误。

      但是,当我将sum 函数更改为使用简单的测试函数进行编译时:

      let inline sum< ^a when 
              ^a : (static member Zero : ^a) and
              ^a : (static member (+) : (^a -> ^a -> ^a))  > : ^a list -> ^a = 
          reduce (fun i j -> i)
      

      并尝试调用sum [1..10] 导致The type 'int' does not support the operator 'get_op_Addition'。所以我不确定这种方法是否会奏效。

      编辑:感谢 CaringDev 的提示,我修复了 get_op_Addition 错误。这似乎有效。虽然至于为什么,我很困惑,因为'b 只是伪装的^a...

      let inline sum< ^a when 
              ^a : (static member Zero : ^a) and
              ^a : (static member (+) : ^a -> ^a -> ^a)  > : ^a list -> ^a =
          let inline add (x:'b) (y:'b) = (+) x y
          reduce add
      

      【讨论】:

      • (a.k.a. Vandroiy) 我好像有了一个新昵称^^
      • Vandroiy 发布了一个答案,然后他删除了它,所以现在你必须足够了哈哈。
      • 此错误源于+ 约束的多余括号:这些使编译器相信您希望成员获取(即返回)+ 函数。
      • @CaringDev 我什至没有达到那个(+)约束;给我这个错误的代码不太可能是未键入的代码
      • @Sehnsucht,是的,你的问题是由于问题中的错误。我开始删除所有注释......所以甚至没有注意到:-) @Ringils The type 'int' does not support ... 是由于static member (+) : (^a -&gt; ^a -&gt; ^a) 而不是static member (+) : ^a -&gt; ^a -&gt; ^a
      猜你喜欢
      • 1970-01-01
      • 2020-12-20
      • 2022-01-22
      • 1970-01-01
      • 1970-01-01
      • 2019-06-10
      • 2018-02-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多