非惯用的全局运算符重载如何工作?

2023-11-29

我想了解代码this answer

type Mult = Mult with
    static member inline ($) (Mult, v1: 'a list) = fun (v2: 'b list) -> 
        v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
    static member inline ($) (Mult, v1:'a      ) = fun (v2:'a) -> v1 * v2 :'a

let inline (*) v1 v2 = (Mult $ v1) v2

F# 可以解析重载的成员。 (因为它不支持成员柯里化)。所以,我想,它也应该适用于方法

但它没有:

type Mult = Mult with
        static member inline Do (Mult, v1: 'a list) = fun (v2: 'b list) -> 
            v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
        static member inline Do (Mult, v1:'a      ) = fun (v2:'a) -> v1 * v2 :'a
    let inline (<.>) v1 v2 = (Mult.Do (Mult,v1)) v2

无法根据以下内容确定方法“Do”的唯一重载 在此程序点之前键入信息。类型注释可以是 需要。候选:静态成员 Mult.Do : Mult:Mult * v1: ^a -> ( ^a -> ^a) 当 ^a : (静态成员 ( * ) : ^a * ^a -> ^a) 时,静态成员 Mult.Do : Mult:Mult * v1:'a list -> ('b list -> ('a*'b) 列表)

其中运算符的语法$的定义令人困惑。它接受大写标识符作为运算符的第一个参数,并且 Visual Studio 不会抱怨它

Mult 被推断为 mult 类型,但令人惊讶的是这不起作用:

type Mult = Mult with
    static member inline (!!) (mlt:Mult, v1: 'a list) = fun (v2: 'b list) -> 
        v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
    static member inline (!!) (mlt:Mult, v1:'a      ) = fun (v2:'a) -> v1 * v2 :'a

let inline (<!>) v1 v2 = (Mult !! v1) v2

错误 FS0003:该值不是函数,无法应用


您的第二个示例不起作用,因为 F# 不会像使用运算符那样自动推断方法的静态成员约束。

所以是的,这是可能的,但您必须手动编写约束,编译器不会为您推断它们:

type Mult = Mult with
    static member inline Do (Mult, v1: 'a list) = fun (v2: 'b list) -> 
        v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
    static member inline Do (Mult, v1:'a      ) = fun (v2:'a) -> v1 * v2 :'a

let inline impl m v1 v2 = ((^T or ^a) : (static member Do:^T* ^a->(^b-> ^c)) (m,v1)) v2
let inline (<.>) a b = impl Mult a b

您提到的大写标识符与仅包含一种大小写的可区分联合相匹配,因此它始终会成功,并且大小写的名称与类型的名称相同。所有这些都是为了缩短一点代码量,因为 DU 是虚拟类型。如果您感到困惑,这里有一个普通类的示例:

type Mult() = class end with
    static member inline ($) (_:Mult, v1: 'a list) = fun (v2: 'b list) -> 
        v1 |> List.collect (fun x -> v2 |> List.map (fun y -> (x, y))) : list<'a * 'b>
    static member inline ($) (_:Mult, v1:'a      ) = fun (v2:'a) -> v1 * v2 :'a

let inline (*) v1 v2 = (Mult() $ v1) v2

你的第三个例子不起作用,因为(!!)是一元运算符,而不是二元运算符($)

有关此旧技术的更多信息,请参见这个旧博客.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

非惯用的全局运算符重载如何工作? 的相关文章

随机推荐