Overloaded inline operators in F#: ( |+| )

2019-01-12 10:21发布

I'm trying to define an overloaded operator, e.g. |+|, as the following:

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

The problem is, I can't do something like:

let three = m1 |+| m2 |+| m3

Because the operator |+| isn't defined for the case (m1 : int) (m2 : #IMeasurable). Is there a way to overload this operator or use static type constraints to make the above expression possible? Is there a way to modify IMeasurable (which I can edit) so that this is possible? Anything else that would allow the above expression to work?

Thank you.

2条回答
干净又极端
2楼-- · 2019-01-12 10:58
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

Not tested, since I don't have IMeasurable, but it may do the job.

查看更多
Anthone
3楼-- · 2019-01-12 11:12

If you're defining an operator that behaves like + then I think the best design is to define an operator that returns a value of the same type as is the type of its arguments. This means that I would change the operator to return IMeasurable instead of 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)

This will make the operator definition more uniform and easier to use. The code you wanted to write will now work (returning IMeasurable), but you can also use 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 (|+|)

That said, if you really want to overload an operator that is defined using let and you cannot add it to a type as static member (because you cannot modify the type), then you'll need to use the trick that Gustavo describes

查看更多
登录 后发表回答