Why does compiler complain that MyAdd is not defined?
type MyTest =
static member MyAdd (y1 : int, y2 : int) = y1 + y2
static member Test (x1 : int, x2 : int) =
let Z = MyAdd (x1,x2)
0.0
You need to specify the type when calling a static member:
let Z = MyTest.MyAdd (x1,x2)
Types can't "be open" like modules or namespaces. Members are called via dot-notation:
instance.MyMember for instance members
MyType.MyStaticMember for static members.
Related
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.
The Class:
type NotAbstract () =
member this.WithOptionalParameters (x, ?y) =
let y = defaultArg y 10
x + y
has the following type signature:
type NotAbstract =
class
new : unit -> NotAbstract
member WithOptionalParameters : x:int * ?y:int -> int
end
However, this does not work:
[<AbstractClass>]
type AbstractExample () =
abstract WithOptionalParameters: int * ?int -> int /// Ouch...
type NotAbstract () =
inherit AbstractExample ()
override this.WithOptionalParameters (x, ?y) =
let y = defaultArg y 10
x + y
How to write the proper type signature in the abstract definition of a function with optional parameters? I did not find any hint here.
PS: I am aware that (similar) result could be achieved with polymorphism
Declaring an argument as Option type doesn't really make the argument optional.
NotAbstract().WithOptionalParameters(2)
// This expression was expected to have type
// int * Option<int>
// but here has type
// int
The spec ยง8.13.6 has it:
In a signature, optional arguments appear as follows:
static member OneNormalTwoOptional : arg1:int * ?arg2:int * ?arg3:int -> int
Naming your optional argument in the abstract member signature thus
[<AbstractClass>]
type AbstractExample () =
abstract WithOptionalParameters: int * ?y:int -> int
type NotAbstract () =
inherit AbstractExample ()
override this.WithOptionalParameters (x, ?y) =
let y = defaultArg y 10
x + y
NotAbstract().WithOptionalParameters(42) // val it : int = 52
Optional parameters are compiled to Option types, use Option<int> instead of ?int:
[<AbstractClass>]
type AbstractExample () =
abstract WithOptionalParameters: int * Option<int> -> int
type NotAbstract () =
inherit AbstractExample ()
override this.WithOptionalParameters (x, ?y) =
let y = defaultArg y 10
x + y
This should work:
[<AbstractClass>]
type AbstractExample () =
abstract WithOptionalParameters: int * Nullable<int> -> unit
In F#, there's no syntactical sugar for nullable types, so although you can declare a value nullable with the ?y syntax, you can't do that for a type. Instead, you'll have to use Nullable<T>.
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.
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
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