I am stuck attempting to overload the (*) operator for a Measure type.
What I would like to see is :
> let x = 1.0<i> * 1.0<i>;;
val x : float = -1.0
The following definition appears to do the trick :
> let inline (*) (v1 : float<i>) (v2 : float<i>) = float(-1) * float(v1) * float(v2);;
val inline ( * ) : float<i> -> float<i> -> float
Note that the product measure in this example correctly resolves to <1> as which happens for example when multiplying the imaginary unit of a complex number. Without this overloading definition the default product resolves to < i^2>.
But the overloading definition above has the nasty side effect that :
> let y = 1.0 * 1.0;;
let y = 1.0 * 1.0;;
--------^^^
stdin(11,9): error FS0001: This expression was expected to have type
float<i>
but here has type
float
Apparently my overloading definition hides the (*) operator for the float type.
What am I doing wrong?
Note that you are redefining the (*) operator rather than overloading it.
The trick to get it working is to write something using an intermediate type, like this:
type Mult = Mult with
static member ($) (Mult, v1: float<i>) = fun (v2: float<i>) ->
float(-1) * float(v1) * float(v2)
static member inline ($) (Mult, v1 ) = fun v2 -> v1 * v2
static member ($) (Mult, v1: Mult) = fun () -> Mult //Dummy overload
let inline (*) v1 v2 = (Mult $ v1) v2
BTW funny way to use units of measure.
Related
Given a generic interface declaration like
type IFoo<'T,'MT> =
abstract Return : 'T -> 'MT
abstract Bind : 'MT * ('T -> 'MT) -> 'MT
it's actually possible to use object expressions as computation builder expressions, which could provide for an interesting approach to the division between encapsulation and execution logic of monadic workflows.
let inline addOption mx my =
{ new IFoo<_,_> with
member __.Return x = Some x
member __.Bind(ma, f) = Option.bind f ma }
{ let! x = mx
let! y = my
return x + y }
// val inline addOption :
// mx: ^a option -> my: ^a option -> ^a option
// when ^a : (static member ( + ) : ^a * ^a -> ^a)
addOption (Some 1) (Some 2)
// val it : int option = Some 3
addOption None (Some 2)
// val it : int option = None
The compiler checks on the type of the expression if the expected methods are present. But it is only halfway there; because for real monads, I would need to get the method signature abstract Bind : 'MT * ('T -> 'MU) -> 'MU honoured, a projection to a different non-encapsulated type. Why can't this be done?
The following gives a duplicate definition error:
let (.*) (m1 : Matrix<float>) (m2 : Matrix<float>) =
m1.Multiply(m2)
let (.*) (v1 : Vector<float>) (v2 : Vector<float>) =
v1.DotProduct(v2)
Is there a way to define an operator overload such that F# recognizes the function I'm trying to call based on the function signature?
For example Julia has this very useful feature:
julia> methods(*)
# 138 methods for generic function "*":
*(x::Bool, y::Bool) at bool.jl:38
*{T<:Unsigned}(x::Bool, y::T<:Unsigned) at bool.jl:53
*(x::Bool, z::Complex{Bool}) at complex.jl:122
*(x::Bool, z::Complex{T<:Real}) at complex.jl:129
...
It would be great if there were a way to replicate something similar in F#.
In this specific case * is already overloaded. For example:
let m = matrix [[ 1.0; 4.0; 7.0 ]
[ 2.0; 5.0; 8.0 ]
[ 3.0; 6.0; 9.0 ]]
let v = vector [ 10.0; 20.0; 30.0 ]
let s = 5.
m * m
//val it : Matrix<float> =
// DenseMatrix 3x3-Double
//30 66 102
//36 81 126
//42 96 150
v * v
//val it : float = 1400.0
With overloading:
type Mult = Mult with
static member inline ( $ ) (Mult, m1:Matrix<float>) = fun (m2:Matrix<float>) -> m1.Multiply(m2)
static member inline ( $ ) (Mult, v1:Vector<float>) = fun (v2:Vector<float>) -> v1.DotProduct(v2)
let inline ( .*. ) v1 v2 = (Mult $ v1) v2
And you can use it like this:
m .*. m
v .*. v
You'll get the same results as above. You can use .*, I just avoided it as not to get confused with the already defined .*. Now this is actually discussed in Global Operator Overloading and Oveload operator in F# but F#'s behavior here is a bit obscure so I re-did the example with the Matrix and Vector types. You can probably make it generic. Maybe someone more familiar with mathdotnet can provide a more idiomatic solution. You should also check if * , .* et al. are already overloaded and function as expected for you since for common operations most of this stuff is already implemented.
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
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
I found that:
abs -10
abs -10L
both work. So I wondered how F# implemented this and did a search in the source code:
type AbsDynamicImplTable<'T>() =
let AbsDynamic x = AbsDynamicImplTable<_>.Result x
[<CompiledName("Abs")>]
let inline abs (x: ^T) : ^T =
AbsDynamic x
when ^T : ^T = absImpl x
And I am confused with these.
As I know in a function like abs, we must compare the input with 0, and there are different 0s for different types.
Thanks.
To add some explanation to the code posted by ChaosPandion, the problem with F# functions like abs is that they can work with any numeric type. There is no way to express this just using F#/.NET generics - the only supported constrains are that type parameter implements a certain interface or has a constructor, but there is no constraint for numeric types.
So, F# also supports static constraints (the type parameter is ^a instead of usual 'a) and these are processed at compile time using inlining (this also explains why the function has to be inline). You can write you own function with static constraints by using built-in functions from LanguagePrimitives which contains many useful functions that require some constraints:
> let inline half (num: ^a) : ^a =
LanguagePrimitives.DivideByInt< (^a) > num 2
;;
val inline half : ^a -> ^a
when ^a : (static member DivideByInt : ^a * int -> ^a)
> half 42.0;;
val it : float = 21.0
> half 42.0f;;
val it : float32 = 21.0f
Note that constraints are inferred - DivideByInt requires that the type has DivideByInt member, so our function requires the same thing (and it will work with your own type if it has this member too, which is quite useful!).
In addition to this, the implementation of abs uses two additional tricks that are allowed only in the F# library - it specifies different code (to be used when inlining) for different types (using when ^a:int = ....) and a fallback case, which uses Abs member, so it will work with any explicitly listed type or a type with Abs member. Another trick is the retype function, which "changes the type", but doesn't contain any code - the only purpose is to make the code type-check, but this could be very unsafe - so this is used only in F# libraries.
Actually that Abs table will call this:
let inline abs_impl (x: ^a) : ^a =
(^a: (static member Abs : ^a -> ^a) (x))
when ^a : int32 = let x : int32 = retype x in if x >= 0 then x else -x
when ^a : float = let x : float = retype x in if x >= 0.0 then x else -x
when ^a : float32 = let x : float32 = retype x in if x >= 0.0f then x else -x
when ^a : int64 = let x : int64 = retype x in if x >= 0L then x else -x
when ^a : nativeint = let x : nativeint = retype x in if x >= 0n then x else -x
when ^a : int16 = let x : int16 = retype x in if x >= 0s then x else -x
when ^a : sbyte = let x : sbyte = retype x in if x >= 0y then x else -x
when ^a : decimal = System.Math.Abs(retype x : decimal)