Could not extend operators in F#? - f#

module FSharp=
let Point2d (x,y)= Point2d(x,y)
let Point3d (x,y,z)= Point3d(x,y,z)
type NXOpen.Point3d with
static member ( * ) (p:Point3d,t:float)= Point3d(p.X*t,p.Y*t,p.Z*t)
static member ( * ) (t:float,p:Point3d)= Point3d(p.X*t,p.Y*t,p.Z*t)
static member (+) (p:Point3d,t:float)= Point3d(p.X+t,p.Y+t,p.Z+t)
static member (+) (t:float,p:Point3d)= Point3d(p.X+t,p.Y+t,p.Z+t)
static member (+) (p:Point3d,t:Point3d)= Point3d(p.X+t.X,p.Y+t.Y,p.Z+t.Z)
let a=Point3d (1.,2.,3.)
let b=1.0
let c=a * b//error
Error 15: The type 'float' does not match the type
'Point3d' E:\Work\extension-RW\VS\extension\NXOpen.Extension.FSharp\Module1.fs 18 13 NXOpen.Extension.FSharp
I want to extend the Point3d methods, some new operators. But it doesn't pass over.

If the Point3d type is declared in a separate assembly that you can't modify, then there is (unfortunately) no way to implement new overloads of the standard operators like + or *. The code in your question adds operators as extension methods, but the F# compiler doesn't search for extension methods when looking for overloaded operators.
If you can't modify the library, then there are three things you can do:
Create a wrapper for Point3d that stores a value of Point3d and implements all the operators
(but this is likely going to be quite inefficient)
Define new operators that do not clash with the built-in ones. For example, you can use +$ and $+ for multiplication by scalar from the left and right. To declare such operator, you would write:
let (+$) (f:float) (a:Point3d) = (...)
Implement your own Point3d type that does all the work, possibly with a conversion function that turns it into Point3d when you need to call a library.
It is hard to tell which option is the best - the second approach is probably the most efficient, but it will make code look a bit uglier. Depending on your scenario, the option 1 or 3 may work too.

Indeed it is possible.
There is a way to extend binary operators using the one and only and little known ternary operator ?<-. So in your case you can try this:
type SumPoint3d = SumPoint3d with
static member (?<-) (p:Point3d, SumPoint3d, t ) = Point3d(p.X + t , p.Y + t , p.Z + t )
static member (?<-) (t , SumPoint3d, p:Point3d) = Point3d(p.X + t , p.Y + t , p.Z + t )
static member (?<-) (p:Point3d, SumPoint3d, t:Point3d) = Point3d(p.X + t.X, p.Y + t.Y, p.Z + t.Z)
static member inline (?<-) (a , SumPoint3d, b ) = a + b
type ProdPoint3d = ProdPoint3d with
static member (?<-) (p:Point3d, ProdPoint3d, t ) = Point3d(p.X * t, p.Y * t, p.Z * t)
static member (?<-) (t , ProdPoint3d, p:Point3d) = Point3d(p.X * t, p.Y * t, p.Z * t)
static member inline (?<-) (a , ProdPoint3d, b ) = a * b
let inline ( + ) a b = a ? (SumPoint3d ) <- b
let inline ( * ) a b = a ? (ProdPoint3d) <- b
let a=Point3d (1.,2.,3.)
let b=1.0
Now you can try:
> let c=a * b ;;
val c : Point3d = Point3d (1.0,2.0,3.0)
> 2 * 3 ;;
val it : int = 6

Related

In F#, is it possible to implement operators for tuples?

I am working with an API that represents points like float * float.
These are inconvenient to do arithmetic on:
let a = (3.0, 4.0)
let b = (2.0, 1.0)
let c = (fst a + fst b, snd a + snd b)
I would like to write:
let c = a + b
I can do this if I define my own type:
type Vector2 =
{
X : float;
Y : float;
}
with
static member (+) (a : Vector2, b : Vector2) =
{ X = a.X + b.X; Y = a.Y + b.Y }
But then I need to convert for the API I am using:
let c = a + b
let cAsTuple = (c.X, c.Y)
Alternatively, I could create a free function:
let add (ax, ay) (bx, by) =
(ax + bx, ay + by)
let c = a |> add b
But this is not quite as nice as true infix operators.
Does F# allow me to define custom operators for tuples?
If you are willing to use a different operator like (+.) you can do this:
let inline (+.) (a,b) (c,d) = (a + c, b + d)
it works with ints, floats, strings:
( 4 , 3 ) +. ( 3 , 2 ) // (7, 5)
( 4., 3.) +. ( 3., 2.) // (7.0, 5.0)
("4", "3") +. ("3", "2") // ("43", "32")
TL;DR; #AMieres answer is the real one, this should rather be a comment but comments are length limited and code formatting is not nice ¯_(ツ)_/¯
There is work in progress to make operator extensions become reality: Issue, RFC, PR Once this is done, the following might finally work:
open System
open System.Runtime.CompilerServices
[<Extension>]
type TupleExtensions() =
[<Extension>]
static member inline (+) ((x1, y1), (x2, y2)) = (x1 + x2, y1 + y2)
// or
type Tuple<'T1, 'T2> with
// warning FS1215: Extension members cannot provide operator overloads.
// Consider defining the operator as part of the type definition instead.
static member inline (+) ((x1, y1), (x2, y2)) = (x1 + x2, y1 + y2)
// and then
let t1 = (1., 2.)
let t2 = (42., 3.141)
TupleExtensions.(+) (t1, t2) // (43.0, 5.141)
// error FS0001: Expecting a type supporting the operator '+' but given a tuple type
t1 + t2

F# operator overloading riddle

In F# operator overloading seems powerful but also tricky to get right.
I have following class:
type Value<'T> =
with
static member inline (+) (a : Value<'U>, b: Value<'U>) : Value<'U> =
do stuff
If i define another overload for + with :
static member inline (+) (a : Value<'U>, b: 'U) : Value<'U> =
do stuff
It works. But if i want a symmetric operator:
static member inline (+) (b: 'U, a : Value<'U>) : Value<'U> =
do stuff
The compiler complains:
let a = Value<int>(2);
let b = a + 3 // ok
let c = 3 + a //<-- error here
Error 3 Type inference problem too complicated (maximum iteration depth reached). Consider adding further type annotations
Is there a way around this and stay generic?
I am using F# 3.1
Thanks
The compiler never has a choice: When you apply the (+) operator, you either give it something of type int or something of type Value<'U>. Something of type int cannot be considered of type Value<'U>, and vice versa.
Let's try this in the interpreter. I made the two implementations output A and B so we can tell which is being called:
> type Value<'T> =
- { a : 'T }
- with
- static member inline (+) (a : Value<'U>, b: Value<'U>) : Value<'U> =
- printfn "A"; a
- static member inline (+) (a : Value<'U>, b: int) : Value<'U> =
- printfn "B"; a
- ;;
type Value<'T> =
{a: 'T;}
with
static member ( + ) : a:Value<'U> * b:Value<'U> -> Value<'U>
static member ( + ) : a:Value<'U> * b:int -> Value<'U>
end
Now we have a type. Let's make a value of it.
> let t = { a = "foo" };;
val t : Value<string> = {a = "foo";}
We can now try the overloads. First int:
> t + 4;;
B
val it : Value<string> = {a = "foo";}
Ok. Now Value<string>:
> t + t;;
A
val it : Value<string> = {a = "foo";}
As stated in another answer, my preferred solution would be to use a base class for some overloads:
type BaseValue<'T>(v : 'T) =
member x.V = v
type Value<'T>(v : 'T) =
inherit BaseValue<'T>(v : 'T)
static member inline (+) (a : Value<_>, b: Value<_>) = Value(b.V+a.V)
type BaseValue with
static member inline (+) (a: BaseValue<_>, b) = Value(b+a.V)
static member inline (+) (b, a: BaseValue<_>) = Value(b+a.V)
// test
let v = Value(2)
let a = v + v
let b = v + 3
let c = 3 + v
let d = Value(Value 7) + Value(Value 10)
let e = 5 + 7
Note that this problem disappears if you use the same type argument on your members as you do on the class itself:
type Value<'t when 't : (static member (+) : 't * 't -> 't)> = V of 't with
static member inline (+)(V(x:'t), V(y:'t)) : Value<'t> = V(x+y)
static member inline (+)(V(x:'t), y:'t) : Value<'t> = V(x+y)
static member inline (+)(x:'t, V(y:'t)) : Value<'t> = V(x+y)
This means that you can't create instances of type Value<obj> any more, but might meet your needs.
This operator definition is not sensible, though interestingly, neither is the compiler's reaction.
First of all, think about the intended return type of adding two Value<_> objects. It could be Value<Value<_>>! Language semantics aside, there is no reason to assume that a fully generic type like this shouldn't be nested within itself. This + operator is ill-defined. Type inference will always fail on the overload that adds two Value<_> instances, as the compiler will be unable to resolve the ambiguity.
But the more fundamental question is: what does this operator even mean? Add values and maybe wrap them in an extra type? Those are two entirely different operations; I don't see the merit of combining them, let alone implicitly and in a possibly ambiguous manner. It might save a lot of hassle to only define addition for two Value instances and construct them explicitly where needed.
That aside, it looks like the compiler is quite unintuitive in this case. Someone who knows the F# specification in detail may be able to tell whether it's a bug or inconsistent design, but look at how similar variants behave:
type Value<'T> =
{ Raw : 'T }
static member inline (+) (v, w) = { Raw = v.Raw + w.Raw }
static member inline (+) (v, a) = { Raw = v.Raw + a }
static member inline (+) (a, v) = { Raw = a + v.Raw }
let a = { Raw = 2 }
let b = a + 3 // ok
let c = 3 + a // ok
let d = { Raw = obj() } // No problemo
Too far off? Try this:
type Value<'T> =
val Raw : 'T
new (raw) = { Raw = raw }
static member inline (+) (v : Value<_>, w : Value<_>) = Value(v.Raw + w.Raw)
static member inline (+) (v : Value<_>, a) = Value(v.Raw + a)
static member inline (+) (a, v : Value<_>) = Value(a + v.Raw)
let a = Value(2)
let b = a + 3 // OK
let c = 3 + a // OK
let d = Value(obj) // No problemo
Not sure what is going on here, but it's not very consistent.

F# operator overloading riddle 2

In F# operator overloading seems powerful but also tricky to get right.
I have following class:
type Value<'T> =
with
static member inline (+) (a : Value<'U>, b: Value<'U>) : Value<'U> =
do stuff
If i define another overload for + with :
static member inline (+) (a : Value<'U>, b: 'U) : Value<'U> =
do stuff
It works. But if i want a symmetric operator:
static member inline (+) (b: 'U, a : Value<'U>) : Value<'U> =
do stuff
The compiler complains:
let a = Value<int>(2);
let b = a + 3 // ok
let c = 3 + a //<-- error here
Error 3 Type inference problem too complicated (maximum iteration depth reached). Consider adding further type annotations
Is there a way around this and stay generic?
I am using F# 3.1
Thanks
Removing the type annotations will solve the issue you pointed out, but you didn't notice there is another issue: try invoking the first overload, the compiler will not know which overload to call. It's a shame the overload resolution doesn't pick up the right one.
One tricky way to get everything working at compile time is to declare only the first overload into the type, and for the rest use the trick of redefining the (+) operator using an intermediate type:
type Value<'T> = Value of 'T with
static member inline (+) (Value a, Value b) = Value (a + b)
type Sum = Sum with
static member inline (?<-) (Sum, a, b) = a + b
static member inline (?<-) (Sum, Value a, b) = Value (a + b)
static member inline (?<-) (Sum, b, Value a) = Value (a + b)
let inline (+) a b :'t = (?<-) Sum a b
// test
let v = Value(2)
let a = v + v
let b = v + 3
let c = 3 + v
let d = Value(Value 7) + Value(Value 10)
let e = 5 + 7
UPDATE
I've found another workaround which I prefer since it does not require redefining the (+) operator, the trick is to create a base class and move some overloads there:
type BaseValue<'T>(v : 'T) =
member x.V = v
type Value<'T>(v : 'T) =
inherit BaseValue<'T>(v : 'T)
static member inline (+) (a : Value<_>, b: Value<_>) = Value(b.V+a.V)
type BaseValue with
static member inline (+) (a: BaseValue<_>, b) = Value(b+a.V)
static member inline (+) (b, a: BaseValue<_>) = Value(b+a.V)
// test
let v = Value(2)
let a = v + v
let b = v + 3
let c = 3 + v
let d = Value(Value 7) + Value(Value 10)
let e = 5 + 7
I think the problem lies in the fact that there is no way to resolve between the first and, say, the second overload when the second parameter to the second overload is a Value<'T> itself.
Here is a complete version that causes the error:
type Value<'T>(v : 'T) =
member x.V = v
with
static member inline (+) (a : Value<'U>, b: Value<'U>) : Value<'U> =
Value<'U>(b.V+a.V)
static member inline (+) (a : Value<'U>, b: 'U) : Value<'U> =
Value<'U>(b+a.V)
static member inline (+) (b: 'U, a : Value<'U>) : Value<'U> =
Value<'U>(b+a.V)
let a = Value<int>(2);
let b = a + 3
let c = 3 + a
I'd say write one operator overload, and do pattern matching on type inside it? Ugly, I know.

How to properly overload global (+) and (*) without breaking support for other types

I have imported a vector math library, and would like to add my own (*) and (+) operators while preserving the existing operators for basic int and float.
I have tried the following:
let inline (*) (x : float) (y : Vector) = y.Multiply(x)
let inline (*) (x : Vector) (y : float) = x.Multiply(y)
let inline (+) (x : Vector) (y : Vector) = x.Add(y)
Which has two problems:
It seems to remove int + int and int * int, and
The 2nd line (which is intended to complete commutativity) does not compile because it is a "duplicate definition".
How can I go about defining some commutative operators on my imported Vector type while also not losing these operations on ints and floats?
(I want to be able to write generic code elsewhere using * and +, without having to specify float/Vector/int type constraints).
If you are able to modify source code of the library, it's simpler to add a few overloads via type extensions:
type Vector with
static member (*) (x : Vector) (y : float) = x.Multiply(y)
static member (+) (x : Vector) (y : Vector) = x.Add(y)
However, if the first operand has a primitive type (e.g your first example), overloading resolution doesn't work any more.
At any rate, you can exploit member overloading and propagate constraints to an inline function:
type VectorOverloadsMult =
| VectorOverloadsMult
static member (?<-) (VectorOverloadsMult, x: float, y: Vector) = y.Multiply(x)
static member (?<-) (VectorOverloadsMult, x: Vector, y: float) = x.Multiply(y)
static member inline (?<-) (VectorOverloadsMult, x, y) = x * y
let inline (*) x y = (?<-) VectorOverloadsMult x y
This works for existing types with (*) since we preserve them in the last static member. You can do the same for (+) operator.
let v: Vector = ... // Declare a Vector value
let a = 2.0 * v
let b = v * 2.0
let c = 2 * 3
let d = 2.0 * 3.0
This technique works even when you cannot modify the Vector type.
You need to define the operators inside your type - i.e.
type Vector =
....
static member (+) (x : Vector) (y : Vector) = x.Add(y)
etc.
Then all will work as you expect

C# library overloads ^ operator. How to use ** instead?

The Symbolism library overloads arithmetic operators. Although it's written in C# I can use it from F#:
open Symbolism
let x = new Symbol("x")
let y = new Symbol("y")
let z = new Symbol("z")
printfn "%A" (2*x + 3 + 4*x + 5*y + z + 8*y)
the output:
3 + 6 * x + 13 * y + z
However, it also overloads ^ for powers. This of course doesn't play well with F#.
As a step towards a workaround, I exported a method group for powers:
printfn "%A" (Aux.Pow(x, 2) * x)
output:
x ^ 3
How can I overload ** to use the Aux.Pow method group instead?
I can do something like this:
let ( ** ) (a: MathObject) (b: MathObject) = Aux.Pow(a, b)
And that does work for MathObject values:
> x ** y * x;;
val it : MathObject = x ^ (1 + y)
But Aux.Pow is overloaded for int as well:
public static MathObject Pow(MathObject a, MathObject b)
{ return new Power(a, b).Simplify(); }
public static MathObject Pow(MathObject a, int b)
{ return a ^ new Integer(b); }
public static MathObject Pow(int a, MathObject b)
{ return new Integer(a) ^ b; }
Any suggestions welcome!
You can use the trick described here like this:
open Symbolism
type MathObjectOverloads =
| MathObjectOverloads
static member (?<-) (MathObjectOverloads, a: #MathObject, b: int) = MathObject.op_ExclusiveOr(a, b)
static member (?<-) (MathObjectOverloads, a: #MathObject, b: #MathObject) = MathObject.op_ExclusiveOr(a, b)
static member (?<-) (MathObjectOverloads, a: System.Int32, b: #MathObject) = MathObject.op_ExclusiveOr(a, b)
let inline ( ** ) a b = (?<-) MathObjectOverloads a b
let two = Integer(2)
let three = Integer(3)
two ** three
two ** 3
2 ** three
Unlike in the linked answer, we have to use the (?<-) operator because it's the only operator that can take 3 arguments instead of 2, and we need to overload on both the left and right side of the ^ operator
Here's the same answer but without operators.
It works only in F# 3.0 and you can use any number of parameters.
let inline i3 (a:^a,b:^b,c:^c) = ((^a or ^b or ^c) : (static member threeParams: ^a* ^b* ^c -> _) (a,b,c))
open Symbolism
type MathObjectOverloads =
| MathObjectOverloads
static member threeParams (MathObjectOverloads, a: #MathObject , b: int ) = MathObject.op_ExclusiveOr(a, b)
static member threeParams (MathObjectOverloads, a: #MathObject , b: #MathObject) = MathObject.op_ExclusiveOr(a, b)
static member threeParams (MathObjectOverloads, a: System.Int32, b: #MathObject) = MathObject.op_ExclusiveOr(a, b)
let inline ( ** ) a b = i3(MathObjectOverloads, a, b)

Resources