op_addition的静态成员约束

时间:2016-08-01 14:41:51

标签: f#

我正在尝试使用无点List.fold函数重新发明List.reduceList.suminline以便学习。这就是我所拥有的:

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.

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

3 个答案:

答案 0 :(得分:3)

我不知道如何按照您的意愿完成任务,因为我无法以无点样式调用静态约束的静态成员。请注意,您没有调用(+) ^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:F#编译器赋予名称为“op_Addition”的成员约束特殊状态,因为某些.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

答案 1 :(得分:3)

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

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

只有最后一个编译,(技术上这是因为它&#39;类型函数&#34;,这基本上是一个函数,它传递了隐藏的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 (+)

答案 2 :(得分:1)

与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