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)
Related
Why does the inlining not work in this case?
type TupleBuilder () =
static member inline Cons(a,(b,c)) = (a, b, c)
static member inline Cons(a,(b,c,d)) = (a, b, c, d)
static member inline Cons(a,(b,c,d,e)) = (a, b, c, d, e)
let inline cons h t = TupleBuilder.Cons(h,t)
The call to TupleBuilder.Cons gives me the following compiler error
A unique overload for method 'Cons' could not be determined based on type
information prior to this program point. A type annotation may be needed.
Candidates:
static member TupleBuilder.Cons : a:'a0 * ('a1 * 'a2 * 'a3 * 'a4) -> 'a0 * 'a1 * 'a2 * 'a3 * 'a4,
static member TupleBuilder.Cons : a:'a0 * ('a1 * 'a2 * 'a3) -> 'a0 * 'a1 * 'a2 * 'a3,
static member TupleBuilder.Cons : a:'a0 * ('a1 * 'a2) -> 'a0 * 'a1 * 'a2
Inlining alone doesn't delay the overload decision to the call site.
You need to add a type A or type B at the overload call.
You can do it easily by using a binary operator in this case:
type TupleBuilder () =
static member inline ($) (_:TupleBuilder, (b,c)) = fun a -> (a, b, c)
static member inline ($) (_:TupleBuilder, (b,c,d)) = fun a -> (a, b, c, d)
static member inline ($) (_:TupleBuilder, (b,c,d,e)) = fun a -> (a, b, c, d, e)
let inline cons h t = (TupleBuilder() $ t) h
// val inline cons : h:'a -> t: ^b -> 'c
when (TupleBuilder or ^b) : (static member ( $ ) : TupleBuilder * ^b -> 'a -> 'c)
For more inline fun with tuples, have a look at this old blog post.
type Mul = Mul with member inline __.Op(a: ^a,b: ^a) = a*b
type Div = Div with member inline __.Op(a: ^a,b: ^a) = a/b
type Add = Add with member inline __.Op(a: ^a,b: ^a) = a+b
type Sub = Sub with member inline __.Op(a: ^a,b: ^a) = a-b
let inline op x a b =
(^a: (member Op: ^b * ^b -> ^b) x,a,b)
let inline tup2 a b c d = op Mul a b, op Mul c d
let inline tup2' f a b c d = op f a b, op f c d
let a = tup2 1 2 3.0f 4.0f
//let b = tup2' Mul 1 2 3.0f 4.0f //Gives a type error.
I am wondering if there is a way to make the types do what I want in the example above or if I have finally reached the limitation of F#'s type system. Actually, there is a way to make the above work and that is to put all the types into one DU and then pattern match on the DU type like the following:
type Operation =
| Mul
| Add
| Sub
| Div
member inline t.Op a b =
match t with
| Mul -> a * b
| Add -> a + b
| Sub -> a - b
| Div -> a / b
let inline map' (f: Operation) a b c d =
(f.Op a b, f.Op c d)
map' Mul 1 2 3.0f 4.0f
But assuming the first example worked, it would be a more dynamic solution. It is a pity something like passing a higher order function by name inside an argument and having it inlined on the spot to make it generic is not possible.
This limitation of most modern type systems is well explained in kvb's answer to this question.
Here's a workaround, based on the hack suggested there. In fact it's very similar to your code but less verbose.
type Mul = Mul with static member inline ($) (Mul, a: ^a) = fun (b: ^a) -> a*b
type Div = Div with static member inline ($) (Div, a: ^a) = fun (b: ^a) -> a/b
type Add = Add with static member inline ($) (Add, a: ^a) = fun (b: ^a) -> a+b
type Sub = Sub with static member inline ($) (Sub, a: ^a) = fun (b: ^a) -> a-b
let inline tup2' f a b c d = (f $ a) b, (f $ c) d
let b = tup2' Mul 1 2 3.0f 4.0f
The idea is that instead of defining a function you define a type with a single method (which you already did) in this case it will be an operator which would mean apply.
So instead of doing f x you will write f $ x.
UPDATE
As said before, your code is not far from the solution suggested in that answer.
Here's a working example which is even closer to your original code:
type Mul = Mul with static member inline Op(Mul, a: ^a,b: ^a) = a*b
type Div = Div with static member inline Op(Div, a: ^a,b: ^a) = a/b
type Add = Add with static member inline Op(Add, a: ^a,b: ^a) = a+b
type Sub = Sub with static member inline Op(Sub, a: ^a,b: ^a) = a-b
let inline op x a b = ((^a or ^b): (static member Op: ^a * ^b * ^b -> ^b) (x, a, b))
let inline tup2 a b c d = op Mul a b, op Mul c d
let inline tup2' f a b c d = op f a b, op f c d
let a = tup2 1 2 3.0f 4.0f
let b = tup2' Mul 1 2 3.0f 4.0f //Gives NO type error.
So that's basically your original code but using static methods and using an or in the constraints. By doing this the compiler doesn't solve the constraint early and so it works.
I used the operator because it's less verbose and in this case I like how it reads, since Haskell $ means function application.
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.
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.
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