在F#中重载内联运算符 (|+|)。

4
我将尝试定义一个重载运算符,例如|+|,如下所示:
let inline ( |+| ) (m1 : #IMeasurable) (m2 : #IMeasurable) = m1.Measure + m2.Measure

问题在于,我无法执行以下操作:
let three = m1 |+| m2 |+| m3

因为在 (m1 : int) (m2 : #IMeasurable) 这种情况下,运算符 |+| 未被定义。有没有一种方法可以重载这个运算符或使用静态类型约束使上述表达式成立?有没有一种方法可以修改我可以编辑的 IMeasurable 以使这种情况成为可能?还有其他什么可以让上述表达式工作的方法吗?
谢谢。
2个回答

12
type Overloads = Overloads with
    static member ($) (Overloads, m1: #IMeasurable) = fun (m2: #IMeasurable) -> m1.Measure + m2.Measure 
    static member ($) (Overloads, m1: int) = fun (m2: #IMeasurable) -> m1 + m2.Measure

let inline ( |+| ) m1 m2 = (Overloads $ m1) m2

因为我没有IMeasurable,所以没有测试过,但是它可能会完成任务。


6

如果您正在定义一个与+相似的操作符,我认为最佳设计是定义一个操作符,返回值与其参数的类型相同。这意味着我会更改操作符,使其返回IMeasurable而不是int

type IMeasurable =
  abstract Measure : int

let newMeasure m = 
  { new IMeasurable with 
      member x.Measure = m }

let inline ( |+| ) (m1 : #IMeasurable) (m2 : #IMeasurable) = 
  newMeasure (m1.Measure + m2.Measure)

这将使操作符定义更加统一和易于使用。您想编写的代码现在可以正常工作(返回IMeasurable),但您也可以使用Seq.reduce

// Now you can add multiple measure values without issues
let res = (newMeasure 2) |+| (newMeasure 3) |+| (newMeasure 4)

// Moreover, you can also use `Seq.reduce` which would not work
// with your original operator (because it has wrong type)
let res = [newMeasure 2; newMeasure 3; newMeasure 4] |> Seq.reduce (|+|)

话虽如此,如果您真的想要重载使用let定义的运算符,又无法将其作为静态成员添加到类型中(因为无法修改类型),那么您需要使用Gustavo所描述的技巧。


在这种情况下返回一个 IMeasurable 没有意义,恐怕不行。 - GregRos
嗯,如果返回 IMeasurable 没有逻辑意义,那么你可能需要遵循 @Gustavo 的建议。虽然也许这意味着拥有一个简单的将 IMeasurable 转换为 int 的函数,然后使用标准的 + 运算符进行 int 相加可能是一个不错的选择。自定义运算符很难被发现,而像 m1.Measure + m2.Measure + m3.Measure 这样的表达式既不太长,又容易编写和理解。 - Tomas Petricek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接