How do I create a generic class in F#, with a constraint that the type is a measure?
I've tried this but a2 and b2 do not produce errors:
open Microsoft.FSharp.Data.UnitSystems.SI.UnitNames
type Vector2D_A<[<Measure>] 'u>(x : float<'u>, y : float<'u>) =
member this.X = x
member this.Y = y
type Vector2D_B<'t, [<Measure>] 'u>(x : 't, y : 't) =
member this.X = x
member this.Y = y
type Vector2D_C<'t>(x : 't, y : 't) =
member this.X = x
member this.Y = y
let a1 = Vector2D_A(1.0<metre>, 2.0<metre>)
let b1 = Vector2D_A(1.0<metre>, 2.0<metre>)
let c1 = Vector2D_C(1.0<metre>, 2.0<metre>)
let a2 = Vector2D_A(1.0, 2.0) // should produce an error
let b2 = Vector2D_A(1.0, 2.0) // should produce an error
let c2 = Vector2D_C(1.0, 2.0)
I would like to define a class like any of these three examples (but they do not compile):
1)
type Vector2D_B<'t, [<Measure>] 'u>(x : 't<'u>, y : 't<'u>) =
member this.X = x
member this.Y = y
2)
type Vector2D_B<'t when 't :> 't<[<Measure>]>>(x : 't<'u>, y : 't<'u>) =
member this.X = x
member this.Y = y
3)
type Vector2D_B<'t when 't :> 't<_>(x : 't<'u>, y : 't<'u>) =
member this.X = x
member this.Y = y
Writing 't is equivalent to writing 't<1> - where <1> represents the unit of measure for dimensionless values, which applies implicitly when no other unit of measure is explicitly provided.
Consequently, you can't force the compiler to produce an error message when you don't explicitly provide a unit of measure, since when you do that you're implicitly providing the unit of measure for dimensionless values.
To expand on the example from BillH.
Consider the simple case of a multiplication method:
let mult a b = a * b
this has a signature (for say float) of
float<'a> -> float<'b> -> float<'a'b>
Now if 'a = 1/'b the signature is
float<'a> -> float<1/'a> -> float<1>
Now it is not reasonable for this to throw a compiler error. For example, the particular call could occur only with a specific set of inputs to some higher order function. As a result, there is no easy way to specify some constraints on a unit of measure that is in some way generic (although some are possible in for example a square root function).
Even doing a runtime test for no unit given to a function is hard as units of measure information is thrown away after the code is compiled.
Related
RE: What is the best way to pass generic function that resolves to multiple types
Please read the referenced link before going further below
I am trying to extend the concept and pass a generic function that takes 2 parameters and does something with them.
The static approach works, however the interface based one causes a compile error (see the code lines marked with //error):
The declared type parameter '?' cannot be used here since the type parameter cannot be resolved at compile time.
Does anyone know how to fix it?
module MyModule
type T = Content of int
with
static member (+) ((Content i1), (Content i2)) = Content (i1 + i2)
static member (*) ((Content i1), (Content i2)) = Content (i1 * i2)
type W = { Content: int }
with
static member (+) ({Content = i1}, {Content = i2}) = { Content = i1 + i2 }
static member (*) ({Content = i1}, {Content = i2}) = { Content = i1 * i2 }
type Sum = Sum with static member inline ($) (Sum, (x, y)) = x + y
type Mul = Mul with static member inline ($) (Mul, (x, y)) = x * y
let inline f1 (la: 'a list) (lb: 'b list) reducer =
let a = la |> List.reduce (fun x y -> reducer $ (x, y))
let b = lb |> List.reduce (fun x y -> reducer $ (x, y))
(a, b)
type I = abstract member Reduce<'a> : 'a -> 'a -> 'a
let f2 (la: 'a list) (lb: 'b list) (reducer: I) =
let a = la |> List.reduce reducer.Reduce
let b = lb |> List.reduce reducer.Reduce
(a, b)
let main ()=
let lt = [Content 2; Content 4]
let lw = [{ Content = 2 }; { Content = 4 }]
let _ = f1 lt lw Sum
let _ = f1 lt lw Mul
let _ = f2 lt lw { new I with member __.Reduce x y = x + y} //error
let _ = f2 lt lw { new I with member __.Reduce x y = x * y} //error
0
The problem with your attempt is that you can't use operators + or * on parameters x and y, because it's not known that their type 'a has those operators defined.
To answer your further question in comments about how to achieve it anyway - if you want to use multiplication and addition on any type 'a that the caller chooses, you have to specify that. For an interface method, the only way to do this is by constraining the type parameter 'a, and the only two kinds of constraints that .NET runtime supports are "has a parameterless constructor" and "implements a given interface or inherits from a given class".
The latter one would be useful in your case: make both types implement the interface and then constrain type parameter 'a to implement that interface:
type IArithmetic<'a> =
abstract member add : 'a -> 'a
abstract member mult : 'a -> 'a
type T = Content of int
with
interface IArithmetic<T> with
member this.add (Content y) = let (Content x) = this in Content (x + y)
member this.mult (Content y) = let (Content x) = this in Content (x * y)
type W = { Content: int }
with
interface IArithmetic<W> with
member this.add y = { Content = this.Content + y.Content }
member this.mult y = { Content = this.Content * y.Content }
type I = abstract member Reduce<'a when 'a :> IArithmetic<'a>> : 'a -> 'a -> 'a
// ^^^^^^^^^^^^^^^^^^^^^^^^^^
// the constraint right here
...
let _ = f2 lt lw { new I with member __.Reduce x y = x.add y }
let _ = f2 lt lw { new I with member __.Reduce x y = x.mult y }
Is this a bit awkward? I guess so, but you're kind of doing the same thing for the SRTP version, so why not?
The core idea is: if you want your Reduce method to work not with just any type, but only with types that can do certain things, you have to specify what those things are. In the SRTP case you're doing that by defining the (+) and (*) operators. In the interface case you're doing that by implementing the interface.
Q: But can I make the interface somehow pick up the (+) and (*) operators?
A: In general, no. The .NET runtime just doesn't support the kind of constraints like "any type that has a method with certain signature". This means that such constraints can't be compiled down to IL, which means they can't be used in an interface implementation.
And this is the price you pay for using SRTPs: all those inline functions - they don't get compiled to IL, they always get expanded (inserted, substituted) at use sites. For small, simple functions, this is no big deal. But if your whole program is like that, you might see some unexpected compiled code bloat, potentially translating to slower startup time etc.
Having said all that, I must note that the code you're showing is toy POC kind of code, not intended to solve any real, practical problem. And as such, most musings on it are in danger of being completely useless.
If you have an actual problem in mind, perhaps try sharing it, and somebody would suggest the best solution for that specific case.
In particular, I have a nagging feeling that you might not actually need higher-rank functions (that's what it's called when a function doesn't lose genericity when passed as parameter).
I am trying to make a class which implements a generic .NET IComparer interface for use with the various System.Collections.Generic collections. This IComparer will map the collection's values into an ordering key using an externally provided function.
An example application might be a SortedSet<City> which sorts by population, but where the population is dynamically retrieved from an external data source which is separately maintained and updated.
open System
open System.Collections.Generic
type ExternalComparer<'T>(compareBy: ('T -> 'U) when 'U :> IComparable) =
let compareBy = compareBy
interface IComparer<'T> with
member this.Compare(a, b) =
let x = compareBy a
let y = compareBy b
if x < y then -1 else if x > y then 1 else 0
The compiler issues the following warning on the if x < y expression, specifically on the x:
Warning FS0064 This construct causes code to be less generic than
indicated by the type annotations. The type variable 'U has been
constrained to be type 'IComparable'.
In fact, I intend the type variable 'U to be constrained to be the type 'IComparable,' and I tried to express that intent with the when 'U :> IComparable constraint in the definition of the compareBy function type.
Is this warning message erroneous, or am I doing something wrong?
I think your solution - just accept a function of type 'T -> IComparable is the right thing to do, even if it means that the users of your ExternalComparer might need to insert an upcast to make the code compile.
To explain why you were getting the error, your class has only one generic type parameter 'T and the 'U parameter is missing there - so the compiler constrained it to IComparable. If you wanted to fix that, you'd have to add another generic parameter:
type ExternalComparer<'T, 'U when 'U :> IComparable>(compareBy: ('T -> 'U)) =
let compareBy = compareBy
interface IComparer<'T> with
member this.Compare(a, b) =
let x = compareBy a :> IComparable
let y = compareBy b :> IComparable
if x < y then -1 else if x > y then 1 else 0
Additionally, I also had to add :> IComparable so that we convert the results from whatever 'U is to IComparable that can be compared. This adds another generic parameter, which is quite silly.
If you wanted to avoid that, you could use a static member that converts a 'T -> 'U function to
'T -> IComparable function before passing it to the constructor:
type ExternalComparer<'T>(compareBy: ('T -> IComparable)) =
let compareBy = compareBy
interface IComparer<'T> with
member this.Compare(a, b) =
let x = compareBy a
let y = compareBy b
if x < y then -1 else if x > y then 1 else 0
static member Create(compareBy : 'T -> #IComparable) =
ExternalComparer(fun v -> compareBy v :> IComparable)
Now you can use the Create method nicely:
ExternalComparer<string>(fun v -> int v :> _) // Requires upcast
ExternalComparer<string>.Create(fun v -> int v) // Works directly!
The problem is that you're defining the compareBy type to return a 'U, constrained to IComparable, when you could more clearly have defined it to return an IComparable directly, like this:
type ExternalComparer<'T>(compareBy: ('T -> IComparable)) =
Since you know 'U will always be an IComparable, the use of a generic type is incorrect.
It seems to me you could collapse a chunk of the code into an object expression:
let bigCity = {Name="NYC"; population = 1_000_000}
let smallCity = {Name="KC"; population = 1000}
let cityComparer compFunc = { new System.Collections.Generic.IComparer<'T> with
override __.Compare(c1, c2) =
let x = compFunc c1
let y = compFunc c2
if x < y then -1 else if x > y then 1 else 0
}
let comp = cityComparer (fun x -> x.population)
comp.Compare(smallCity,bigCity)
//val it : int = -1
F# allows ".NET" and "OCaml" formatting of signatures. This can be confusing when you fall into the habit of using one style, and then find a situation where you cannot properly format the signature you need. Consider this code, which requires a flexible type as the output of the function input to foo:
let foo n (bar: int -> #seq<'a>) =
(fun () -> Vector.ofSeq (bar n))
let foobar n = Array.ofSeq([1..n])
let x = foo 10 foobar
I could not figure out how to express #seq<'a> in OCaml format. Is it possible?
The following compiles just fine:
type A<'a>(x) =
member __.Get : 'a = x
abstract PairWith : 'b -> ('a * 'b * int)
default __.PairWith y = x, y, 1
type B<'a>(x) =
inherit A<'a>(x)
override __.PairWith y = x, y, 2
let pairAB (x : #A<'a>) y =
x, x.PairWith y
type 'a X (x) =
member __.Get : 'a = x
abstract PairWith : 'b -> ('a * 'b * int)
default __.PairWith y = x, y, 1
type 'a Y (x) =
inherit X<'a>(x)
override __.PairWith y = x, y, 2
let pairXY (x : #('a X)) y =
x, x.PairWith y
So you can guess (and then confirm with F# Interactive) that you are looking for #('a seq).
I'm not exactly sure what you mean, but I assume that you want to put the type variable in front of the type name, e.g. 'a #seq.
According to the language specification (ยง5.1.5) it's not possible since:
A type of the form #type is an anonymous type with a subtype constraint and is equivalent to 'a when 'a :> type, where 'a is a fresh type inference variable.
So you could write your type like: 'a when 'a :> seq<'b>.
EDIT: You could actually use #('a seq), but it looks awkward and I doubt it's what you want.
EDIT2: Didn't see Ramon Snir's answer :).
I am currently porting some code from Java to F# that deals with multidimensional functions. It supports variable dimension, so in the original implementation each point is represented as an array of doubles. The critical function of the code is an optimisation routine, that basically generates a sequence of points based on some criteria, evaluates a given function at these points and looks for a maximum. This works for any dimension. The operations I need are:
check the dimension of a point
create a new point with the same dimension of a given point
set (in procedural or functional sense) a given coordinate of a point
In F# I could obviously also use arrays in the same way. I was wandering though if there is a better way. If the dimension was fixed in advance, the obvious choice would be to use tuples. Is it possible to use tuples in this dynamic setting though?
No, tuples will be fixed by dimension. Also note that .NET tuples are boxed. If you are operating on large collections of points with small dimension (such as arrays of 2d points), using structs may help.
If you really want to push the F#/.NET advantage over Java, have a look at generics. Writing code with generics allows to write code that works for any dimension, and use different representations for different dimensions (say structs for 1-3 dimensions, and vectors for larger dimensions):
let op<'T where 'T :> IVector> (x: 'T) =
...
This is only relevant though if you are willing to go a long way to get the absolutely best performance and generality. Most projects do not need that, stick with the simplest thing that works.
For the fun of it, here is an extended example of how to utilize generics and F# inlining:
open System.Numerics
type IVector<'T,'V> =
abstract member Item : int -> 'T with get
abstract member Length : int
abstract member Update : int * 'T -> 'V
let lift<'T,'V when 'V :> IVector<'T,'V>> f (v: 'V) : 'V =
if v.Length = 0 then v else
let mutable r = v.Update(0, f v.[0])
for i in 1 .. v.Length - 1 do
r <- r.Update(i, f v.[i])
r
let inline norm (v: IVector<_,_>) =
let sq i =
let x = v.[i]
x * x
Seq.sum (Seq.init v.Length sq)
let inline normalize (v: 'V) : 'V =
let n = norm v
lift (fun x -> x / n) v
[<Struct>]
type Vector2D<'T>(x: 'T, y: 'T) =
member this.X = x
member this.Y = y
interface IVector<'T,Vector2D<'T>> with
member this.Item
with get (i: int) =
match i with
| 0 -> x
| _ -> y
member this.Length = 2
member this.Update(i: int, v: 'T) =
match i with
| 0 -> Vector2D(v, y)
| _ -> Vector2D(x, v)
override this.ToString() =
System.String.Format("{0}, {1}", x, y)
[<Sealed>]
type Vector<'T>(x: 'T []) =
interface IVector<'T,Vector<'T>> with
member this.Item with get (i: int) = x.[i]
member this.Length = x.Length
member this.Update(i: int, v: 'T) =
let a = Array.copy x
a.[i] <- v
Vector(a)
override this.ToString() =
x
|> Seq.map (fun e -> e.ToString())
|> String.concat ", "
[<Struct>]
type C(c: Complex) =
member this.Complex = c
static member Zero = C(Complex(0., 0.))
static member ( + ) (a: C, b: C) = C(a.Complex + b.Complex)
static member ( * ) (a: C, b: C) = C(a.Complex * b.Complex)
static member ( / ) (a: C, b: C) = C(a.Complex / b.Complex)
override this.ToString() = string c
let v1 = Vector2D(10., 30.)
normalize v1
|> printfn "%O"
let v2 = Vector2D(C(Complex(1.25, 0.8)), C(Complex(0.5, -1.)))
normalize v2
|> printfn "%O"
let v3 = Vector([| 10.; 30.; 50.|])
normalize v3
|> printfn "%O"
Note that norm and normalize are fairly general, they cope with specialized 2D vectors and generalized N-dimensional vectors, and with different component types such as complex numbers (you can define your own). The use of generics and F# inlining ensure that while general, these algorithms perform well for the special cases, using compact representations. This is where F# and .NET generics shine compared to Java, where you are obliged to create specialized copies of your code to get decent performance.
I have this type:
type Vector3<[<Measure>]'u> =
struct
val x:float32<'u>
val y:float32<'u>
val z:float32<'u>
end
new(x, y, z) = { x = x; y = y; z = z }
When I try to make a default instance, it gives me a strange error that I couldn't find any information on Google about:
Error 5 The default, zero-initializing constructor of a struct type may only be used if all the fields of the struct type admit default initialization
So okay, I can just use the default constructor to set everything to 0 with. Or not.
let f = Vector3(0.f<N>, 0.f<N>, 0.f<N>)
Gives me an error:
Error 1 This expression was expected to have type float32 but here has type float32
This only seems to happen when I use this instance in a subsequent calculation; otherwise, it correctly resolves the type of f as being Vector3<N>. Giving the constructor the type, as in Vector3<N>(...) seems to also solve the problem, which is really strange.
Is there something I'm doing wrong?
There must be something wrong elsewhere in your code. If you reset F# Interactive, open a new empty F# Script file and paste the following code (and then run it in FSI), then everything works just fine for me:
type Vector3<[<Measure>]'u> =
struct
val x:float32<'u>
val y:float32<'u>
val z:float32<'u>
end
new(x, y, z) = { x = x; y = y; z = z }
[<Measure>] type N
let f = Vector3(0.f<N>, 0.f<N>, 0.f<N>)
I would recommend writing the code using implicit constructor syntax which is more succinct and more idiomatic F# (the struct .. end declaration is still allowed, but it has been used mainly in old versions of F#). Default constructor doesn't seem to work in this scenario, but you can use static member:
[<Struct>]
type Vector3<[<Measure>]'u>(x:float32<'u>, y:float32<'u>, z:float32<'u>) =
member this.X = x
member this.Y = y
member this.Z = z
static member Empty : Vector3<'u> = Vector3(0.f<_>, 0.f<_>, 0.f<_>)
[<Measure>] type N
let f1 = Vector3<N>.Empty
let f2 = Vector3(0.f<N>, 0.f<N>, 0.f<N>)
f1.X + f2.X
You need to specify the Default Value attribute for val fields:
type Vector3<[<Measure>]'u> =
struct
[<DefaultValue(false)>] val mutable x:float32<'u>
[<DefaultValue(false)>] val mutable y:float32<'u>
[<DefaultValue(false)>] val mutable z:float32<'u>
end
member X.Init(x,y,z) =
X.x <- x
X.y <- y
X.z <- z
Or use record types:
type Vector3<[<Measure>]'u> = { x : float32<'u>; y : float32<'u> ; z : float32<'u> }
[<Measure>] type N
let v = { x = 10.0F<N>; y = 10.0F<N>; z = 10.0F<N> }
UPDATE:
type Vector3<[<Measure>]'u> =
struct
val x:float32<'u>
val y:float32<'u>
val z:float32<'u>
new(X, Y, Z) = { x = X; y = Y; z = Z }
end