F# operator overloading riddle - f#

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.

Related

How to constrain type parameter must be an algebraic type (int, float, BigInteger, BigRational, ...)

-- While there are some questions on the net wrt. type constraints already, I didn't find one that can help me solving my issue. --
Goal: I want to create my own Vector/Matrix types, but so, that the implementation does not lock in to a speicific BigRational (or alike) type. All I'd prefer to require is the standard algebraic operations on such types (+ - * / % equality).
open System
type Foo<'T> (value: 'T) =
member inline __.Value : 'T = value
static member inline Add (a: Foo<'T>) (b: Foo<'T>) =
Foo<'T>(a.Value + b.Value)
module Foo =
let inline Create (v) = Foo(v)
let log (foo: #Foo<_>) =
printfn "Foo: %s" (foo.Value.ToString())
[<EntryPoint>]
let main argv =
Foo.log (Foo.Create("hi ho"))
Foo.log (Foo<int>(31415))
Foo.log (Foo<float>(3.1415))
Foo.log (Foo<int>.Add (Foo.Create(3)) (Foo.Create(4)))
let a = Foo.Create(13)
let b = Foo.Create(3.1415)
Foo.log (Foo<int>.Add (a.Value) (a.Value))
Foo.log (Foo<float>.Add (b.Value) (b.Value))
0 // return an integer exit code
I cannot get this tiny example code to compile for more than one single type, such as Foo<int> as well as Foo<float>. How could I do it right?
Many thanks in advance,
Christian.
You almost have it, actually.
In order to create a function that accepts any type that has a + operator, this function must have statically-resolved type parameters (SRTP). For this, it must be inline, which your Add is so that's ok. However here Add is not a generic method: it's a method on the generic type Foo<'T>, so it receives its 'T parameter from it. And a type cannot have SRTP.
A simple fix is to move Add from being a method on the type Foo<'T> to being a function in the module Foo. Then it will become actually generic.
open System
type Foo<'T> (value: 'T) =
member inline __.Value : 'T = value
module Foo =
let inline Create (v) = Foo(v)
let inline Add (a: Foo< ^T>) (b: Foo< ^T>) =
Foo< ^T>(a.Value + b.Value)
let log (foo: #Foo<_>) =
printfn "Foo: %s" (foo.Value.ToString())
[<EntryPoint>]
let main argv =
Foo.log (Foo.Create("hi ho"))
Foo.log (Foo<int>(31415))
Foo.log (Foo<float>(3.1415))
Foo.log (Foo.Add (Foo.Create(3)) (Foo.Create(4)))
let a = Foo.Create(13)
let b = Foo.Create(3.1415)
Foo.log (Foo.Add a a)
Foo.log (Foo.Add b b)
0
I think all you need is the inline keyword if you just want to propagate the member constraints of F#'s overloaded arithmetic operators.
type Foo<'T> (value : 'T) =
member __.Value = value
static member inline (+) (a : Foo<_>, b : Foo<_>) = Foo(a.Value + b.Value)
// static member
// ( + ) : a:Foo< ^a> * b:Foo< ^b> -> Foo< ^c>
// when ( ^a or ^b) : (static member ( + ) : ^a * ^b -> ^c)
Now you can add two Foos of the same type that support a static member (+), Foo 3 + Foo 4 or Foo 3.14 + Foo 3.14, even Foo(Foo 3) + Foo(Foo 4); but not Foo 3 + Foo 3.14. You may still instantiate types that do not possess such member.

Error on Extension Methods when Inlining

I want to extend some system types and later use them via inlining
type System.String with
member this.foo n = this + "!" + n
type System.Boolean with
member this.foo n = sprintf "%A!%A" this n
Now I call these extension methods
let x = "foo".foo "bar"
let y = true.foo "bar"
which gives me this
- val x : System.String = "foobar"
- val y : string = "true!"bar""
All fine and dandy - but now I want to wrap the call to .foo into an inline function
let inline foo n v = (^T : (member foo : ^N -> ^S) v, n)
let z = foo "bar" "baz"
Only now I get a compiler error telling me that
> The type 'string' does not support the operator 'foo':
well ... it does!
Can somebody explain whats going on?
Extension methods are not taken into account in static member constraints (possible duplicate of this) and this is a general problem when you want to implement generic code using member constraints and make it work also with already defined or primitive types.
See the user voice request, also the workarounds mentioned here and Don Syme's explanation of why it's complicated to implement it in the F# compiler.
If you follow the links there you will see currently the way to workaround it basically involves creating an intermediate type and overloads for all the known types and a generic one for the extensions.
This is a very basic example of how to workaround it:
type Foo = Foo with
static member ($) (Foo, this:int) = fun (n:int) -> this + n
static member ($) (Foo, this:string) = fun n -> this + "!" + n
static member ($) (Foo, this:bool) = fun n -> sprintf "%A!%A" this n
let inline foo this n = (Foo $ this) n
//Now you can create your own types with its implementation of ($) Foo.
type MyType() =
static member ($) (Foo, this) =
fun n -> printfn "You called foo on MyType with n = %A" n; MyType()
let x = foo "hello" "world"
let y = foo true "world"
let z = foo (MyType()) "world"
You can enhance it by adding an explicit generic overload for new types:
// define the extensions
type System.String with
member this.foo n = this + "!" + n
type System.Boolean with
member this.foo n = sprintf "%A!%A" this n
// Once finished with the extensions put them in a class
// where the first overload should be the generic version.
type Foo = Foo with
static member inline ($) (Foo, this) = fun n -> (^T : (member foo : ^N -> ^S) this, n)
static member ($) (Foo, this:string) = fun n -> this.foo n
static member ($) (Foo, this:bool) = fun n -> this.foo n
// Add other overloads
static member ($) (Foo, this:int) = fun n -> this + n
let inline foo this n = (Foo $ this) n
//later you can define any type with foo
type MyType() =
member this.foo n = printfn "You called foo on MyType with n = %A" n; MyType()
// and everything will work
let x = foo "hello" "world"
let y = foo true "world"
let z = foo (MyType()) "world"
You can further refine it by writing the static constraints by hand and using a member instead of an operator (see an example here),
At the end of the day you will end up with something like this generic append function from FsControl.
Statically resolved type constraints do not support extension methods. It's just not a feature of F#.
If you would like F# to gain support for higher-kinded polymorphism, you can vote for it on user voice.

overload dereference (!) and assignment (:=) operators in f#

I'm trying to overload the dereference (!) and assignment (:=) operators but not globally. I still want to keep the usual ref op overloads. Here's some code to illustrate the issues:
type MyVar<'a>(init:'a) =
let mutable _value = init
member __.Get() = _value
member __.Set x = _value <- x
//static member (!) (s:MyVar<'a>) = s.Get() // compiles, doesn't work
//static member (:=) (d:MyVar<'a>, s) = d.Set(s) // warning, doesn't work
//let inline (!) (x :MyVar<'a>) = x.Get() // overrides !ref
//let inline (:=) (x :MyVar<'a>) (v :'a) = x.Set(v) // overrides ref := v
let inline (!!) (x :MyVar<'a>) = x.Get() // works but ugly
let inline (.=) (x :MyVar<'a>) (v :'a) = x.Set(v) // works ... meh
let test_myvar() =
let mv = new MyVar<_>("wee")
let r = ref 100
let x = !mv
let y = !!mv
let z = !r
mv .= "haaa"
r := 42
Solution:
#Carsten's solution is what I was looking for & works. However, it turns out that I am using Websharper which compiles using Quotations and #Carstens solution becomes a little more complex. Since Websharper.UI.Next includes that solution, all I had to was include in my project, and it works!
you can get this to work with static constraints - by overloading the (!) and (:=) operators as you tried:
type MyVar<'a>(init:'a) =
let mutable _value = init
member __.Value with get () = _value and set v = _value <- v
let inline (!) a =
(^a : (member Value : ^b) a)
let inline (:=) a v =
(^a : (member Value : ^b with set) (a, v))
I removed your accessors because I only need the same as Ref<'a> has (but you can re-add them)
Demonstration
Here is an F#-interactive session demonstrating this with your values:
val mv : MyVar<string>
val r : int ref = {contents = 100;}
> !mv;;
val it : string = "wee"
> !r;;
val it : int = 100
> mv := "It works";;
val it : unit = ()
> !mv;;
val it : string = "It works"
> r := 50;;
val it : unit = ()
> !r;;
val it : int = 50
remark
I'm not sure if I would really do this though - you only reinvent the Ref-cell (as an class) and gain nothing and of course it might be hard to read for others - so treat with care.

Extension method with F# function type

The MSDN doc on Type Extensions states that "Before F# 3.1, the F# compiler didn't support the use of C#-style extension methods with a generic type variable, array type, tuple type, or an F# function type as the “this” parameter." (http://msdn.microsoft.com/en-us/library/dd233211.aspx)
How can be a Type Extension used on F# function type? In what situations would such a feature be useful?
Here is how you can do it:
[<Extension>]
type FunctionExtension() =
[<Extension>]
static member inline Twice(f: 'a -> 'a, x: 'a) = f (f x)
// Example use
let increment x = x + 1
let y = increment.Twice 5 // val y : int = 7
Now for "In what situations would such a feature be useful?", I honestly don't know and I think it's probably a bad idea to ever do this. Calling methods on a function feels way too JavaScript-ey, not idiomatic at all in F#.
You may simulate the . notation for extension methods with F#'s |> operator. It's a little clumsier, given the need for brackets:
let extension f x =
let a = f x
a * 2
let f x = x*x
> f 2;;
val it : int = 4
> (f |> extension) 2;;
val it : int = 8
> let c = extension f 2;; // Same as above
val c : int = 8

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.

Resources