F# missing type constraint - f#

In the following code, note the type constraint for get_Zero:
type Wrapper<'t> = { Data : 't[] }
let compute<'t
when 't : (static member get_Zero : unit -> 't)
and 't : (static member (~-) : 't -> 't)
and 't : (static member (+) : 't * 't -> 't)>
(wrapper : Wrapper<'t>) =
wrapper.Data
|> Seq.mapi (fun i value -> (i, value))
|> Seq.sumBy (fun (i, value) ->
if i % 2 = 0 then value
else -value)
Even though I already have an explicit type constraint, I'm still getting the following compiler error on the call to Seq.sumBy:
A type parameter is missing a constraint 'when ^t : (static member
get_Zero : -> ^t)'
Anyone know what's going on here? Thanks.

Trying to make downstream static member constraints explicit can be an exercise in frustration, and, fortunately, it's seldom necessary. Just mark the function inline and let them be inferred.
let inline compute (wrapper : Wrapper<_>) =
wrapper.Data
|> Seq.mapi (fun i value -> (i, value))
|> Seq.sumBy (fun (i, value) ->
if i % 2 = 0 then value
else -value)
The correct signature is:
let inline compute<'t
when 't : (static member Zero : 't)
and 't : (static member (~-) : 't -> 't)
and 't : (static member (+) : 't * 't -> 't)>
(You'll notice the signature in the error message isn't even valid syntax: when ^t : (static member get_Zero : -> ^t). This is part of what I mean by frustrating.)

Related

Getting error FS0192: internal error: unexpected non-generation of witness when executing class definition in interactive

I have a class with minimal reproducible implementation looking like that:
type Piecewise<'U, 'V when 'U: comparison
and 'V : (static member (+): 'V * 'V -> 'V)
and 'V : (static member (*): 'V * 'V -> 'V)>
([<System.ParamArray>] pwArr: ('U * 'U * 'V) array) =
member inline this.definition = pwArr
static member inline map (mapping: 'V -> 'W) (pw: Piecewise2<'U, 'V>) =
pw.definition
|> Array.map (fun (b1, b2, v) -> b1, b2, mapping v )
|> fun x -> Piecewise x
and map member definition causes the error mentioned above when I try to commit this to F# interactive.
This class overloads (+) and (*) operators in dowstream members so I had to include them in type constraints.
If I set a weaker contraints on types for map member like that:
static member inline map (mapping: _ -> _) (pw: Piecewise<_, _>) = ...
it commits well up to map member but I'm having errors on overloads of (+) operator:
static member inline (+) (pw: Piecewise<'U, 'V>, u: 'V) =
Piecewise.map ((+) u) pw
//Polynom.fsx(120,28): warning FS0193: A type parameter is missing a constraint 'when ( ^V or ^?17597) : (static member (+) : ^V * ^?17597 -> ^?17598)'
//Polynom.fsx(120,9): error FS0071: Type constraint mismatch when applying the default type 'obj' for a type inference variable. The type 'obj' does not support the operator '+' Consider adding further type constraints
I'm using F# 6.0 in VS 17.05 on Win 10
When I calculate the value for definition field in the let binding first and then assign it to the field the class executes well:
type Piecewise<'U, 'V when 'U: comparison
and 'V : (static member (+): 'V * 'V -> 'V)
and 'V : (static member (*): 'V * 'V -> 'V)>
([<System.ParamArray>] pwArr: ('U * 'U * 'V) array) =
let definition' =
pwArr
|> Array.map (fun (b1, b2, v) ->
if b1 > b2 then b2, b1, v else b1, b2, v )
|> Array.sortBy (fun (b1, _, _) -> b1)
member inline this.definition =
definition'
member inline this.Evaluate(u: 'U) =
this.definition
|> Array.choose(fun (b1, b2, v) ->
if b1 <= u && u < b2 then Some v else None
)
static member inline map (mapping: 'V -> 'W) (pw: Piecewise<'U, 'V>) =
pw.definition
|> Array.map (fun (b1, b2, v) -> b1, b2, mapping v )
|> fun x -> Piecewise x
Still don't really understanding why this works while the direct assignment doesn't

Assign a name to a set of type constraints in F#?

Suppose I have a generic type with some complex type constraints in F#:
[<Struct>]
type Vec2<'t when 't : equality
and 't : comparison
and 't : (static member get_Zero : Unit -> 't)
and 't : (static member (+) : 't * 't -> 't)
and 't : (static member (-) : 't * 't -> 't)
and 't : (static member (*) : 't * 't -> 't)
and 't : (static member (/) : 't * 't -> 't)> =
{
X : 't
Y : 't
}
Now I want to create another generic type that builds on this:
// Does not work
[<Struct>]
type AABB<'t> =
{
Min : Vec2<'t>
Max : Vec2<'t>
}
This does not work unless I replicate the type constraints:
[<Struct>]
type AABB<'t when 't : equality
and 't : comparison
and 't : (static member get_Zero : Unit -> 't)
and 't : (static member (+) : 't * 't -> 't)
and 't : (static member (-) : 't * 't -> 't)
and 't : (static member (*) : 't * 't -> 't)
and 't : (static member (/) : 't * 't -> 't)> =
{
Min : Vec2<'t>
Max : Vec2<'t>
}
This gets old fast!
Is there a way to bind the type constraints to a name so that I can reuse them throughout my code?
// Not real code
constraint IsNumeric 't =
't : equality
and 't : comparison
and 't : (static member get_Zero : Unit -> 't)
and 't : (static member (+) : 't * 't -> 't)
and 't : (static member (-) : 't * 't -> 't)
and 't : (static member (*) : 't * 't -> 't)
and 't : (static member (/) : 't * 't -> 't)
[<Struct>]
type Vec2<'t when IsNumeric 't> =
{
X : 't
Y : 't
}
[<Struct>]
type AABB<'t when IsNumeric 't> =
{
Min : Vec2<'t>
Max : Vec2<'t>
}
A reasonable workaround in this case is to create an interface that represents the constraints. This does not automatically work as named constraint, but you can define a helper function that captures the required operations and then pass the interface around (so that you can invoke the operations you want).
Let's say we want just addition and multiplication:
type INumericalOps<'T> =
abstract Add : 'T * 'T -> 'T
abstract Mul : 'T * 'T -> 'T
[<Struct>]
type Vec2<'T> =
{ X : 'T
Y : 'T }
[<Struct>]
type AABB<'T, 'O when 'O :> INumericalOps<'T>> =
{ Min : Vec2<'T>
Max : Vec2<'T>
Ops : 'O }
Now, the AABB type also contains an implementation of the INumericalOps interface, which is somewhat shorter than specifying all the constraints. We can create an inline function that captures the implementation of * and + for any type that supports those:
let inline capture () =
{ new INumericalOps<_> with
member x.Add(a, b) = a + b
member x.Mul(a, b) = a * b }
When creating a value, the type inference will make sure we get the right implementation of numerical operations:
let aabb =
{ Min = { X = 1.0; Y = 2.0 }
Max = { X = 1.0; Y = 2.0 }
Ops = capture() }
I believe this is essentially this request https://github.com/fsharp/fslang-suggestions/issues/641
Short answer, not supported in F# at this stage but I think it would be a great feature to add.
Your example looks like generic math. This thing will be supported in net6 as preview feature. This has advantages over all others approaches: native support in runtime, C# support it (more libs available), no runtime cost (performance is same as with direct call)
Even though there's no support for this in F# yet, it could look like this (not final, syntax may change)
type IAdditionOperators<'a> =
static abstract (+) : 'a * 'a -> 'a
type IAdditionIdentity<'a> =
inherit IAdditionOperators<'a>
static abstract Zero : 'a
/// This vector type supports addition of 2 generic values
[<Struct>]
type Vector2<'a when 'a : IAdditionIdentity<'a>>(x, y) =
member val X = x
member val Y = y
static member (+) (left, right) =
Vector<'a>('a.(+)(left.X, right.X), 'a.(+)(left.Y, right.Y))
/// This vector supports all methods from INumber
[<Struct>]
type RichVector2<'a when 'a : INumber<'a>>(x, y) =
member val X = x
member val Y = y
static member (+) (left, right) =
Vector<'a>('a.(+)(left.X, right.X), 'a.(+)(left.Y, right.Y))
interface INumber<'a> with
static member (+) (left, right) =
RichVector<'a>.(+)(left, right)
// Requires implementation for +, -, *, /, DivRem,
// Zero, One, Abs, Max, Min, Sign, Clamp,
// Create, CreateSaturating, CreateTruncating, TryCreate,
// Parse, TryParse

Static member constraint for op_addition

I am trying to reinvent List.fold, List.reduce, and List.sum using pointfree inline functions for the sake of learning. Here is what I had:
let flip f y x = f x y
let rec fold folder seed = function
| [] -> seed
| h :: t -> fold folder (folder seed h) t
let inline reduce< ^a when ^a : (static member Zero : ^a) > :
(^a -> ^a -> ^a) -> ^a list -> ^a =
flip fold LanguagePrimitives.GenericZero
let inline sum< ^a when
^a : (static member (+) : ^a * ^a -> ^a) and
^a : (static member Zero : ^a)> : ^a list -> ^a =
reduce (+)
// ^ Compile error here
I know such verbose type constraints are not frequently used in F# but I am trying to learn so please bear with me.
I got a very cryptic compile error which I do not understand:
Type mismatch. Expecting a
'a -> 'a -> 'a
but given a
'a -> 'b -> 'c
A type parameter is missing a constraint 'when ( ^a or ^?269978) : (static member ( + ) : ^a * ^?269978 -> ^?269979)'
A type parameter is missing a constraint 'when ( ^a or ^?269978) : (static member ( + ) : ^a * ^?269978 -> ^?269979)'
val ( + ) : x:'T1 -> y:'T2 -> 'T3 (requires member ( + ))
Full name: Microsoft.FSharp.Core.Operators.( + )
Overloaded addition operator
x: The first parameter.
y: The second parameter.
What should I add to satisfy the compiler?
I don't know a way of doing exactly as you want, because there's no way I know of to invoke a statically constrained static member in pointfree style. Note that you are not invoking (+) of ^a and the compiler does not know how to figure that out... if you are happy using a inner helper function, you can do:
let inline sum< ^a
when ^a : (static member (+) : ^a -> ^a -> ^a) and
^a : (static member Zero : ^a) > : ^a list -> ^a =
let add x y = (^a : (static member (+) : ^a -> ^a -> ^a) (x, y))
reduce add
Note the warning
warning FS0077: Member constraints with the name 'op_Addition' are given special status by the F# compiler as certain .NET types are implicitly augmented with this member. This may result in runtime failures if you attempt to invoke the member constraint from your own code.
and indeed:
sum [1; 2; 3] leads to
System.NotSupportedException: Specified method is not supported.
at FSI_0005.it#15-1.Invoke(Int32 x, Int32 y) in ...
But if you change sum to:
let inline sum list =
reduce (+) list
sum [1; 2; 3] // 6
My unhelpful answer would be: don't do that. Inline definitions are only intended to be syntactic functions, so you're not supposed to define them in a point-free way. For instance, compare the results of trying to evaluate the following three lines:
let inline identity = id
let inline identity : ^a -> ^a = id
let inline identity< ^a> : ^a -> ^a = id
Only the final one compiles, (technically this is because it's a "type function", which is basically a function that's passed an invisible unit value behind the scenes to allow it to be treated as a generic value).
However, if you insist on doing things this way, then one way to "fix" it is to make (+) less generic; by default the arguments and return type can be of different types and the constraints introduced by this flexibility are part of what's confusing the compiler. Here's one fix:
let inline reduce< ^a, 'b when ^a : (static member Zero : ^a)> : ( ^a -> 'b -> ^a) -> ('b list -> ^a) =
flip fold LanguagePrimitives.GenericZero
let inline (+) (x:^a) (y:^a) = x + y
let inline sum< ^a
when ^a : (static member ( + ) : ^a * ^a -> ^a)
and ^a : (static member Zero : ^a)> : ^a list -> ^a =
reduce (+)
Like Vandroiy, the given reduce function doesn't compile for me. I think what you wanted for your reduce function is actually:
let inline reduce< ^a when ^a : (static member Zero : ^a) > : ((^a -> ^a -> ^a) -> ^a list -> ^a) =
flip fold LanguagePrimitives.GenericZero
Doing that I was able to replicate your given error.
However, when I changed the sum function to compile by using a simple test function:
let inline sum< ^a when
^a : (static member Zero : ^a) and
^a : (static member (+) : (^a -> ^a -> ^a)) > : ^a list -> ^a =
reduce (fun i j -> i)
and trying to call sum [1..10] results in The type 'int' does not support the operator 'get_op_Addition'. So I'm not sure this methodology will even work.
EDIT: Thanks for the hints of CaringDev, I fixed the get_op_Addition error. This seems to work. Though as to why, I'm pretty baffled since 'b is just ^a in disguise...
let inline sum< ^a when
^a : (static member Zero : ^a) and
^a : (static member (+) : ^a -> ^a -> ^a) > : ^a list -> ^a =
let inline add (x:'b) (y:'b) = (+) x y
reduce add

F# pattern match using type constraints

Is it possible to do an F# type test pattern with a member constraint?
Such as:
let f x =
match x with
| :? (^T when ^T : (static member IsInfinity : ^T -> bool)) as z -> Some z
| _ -> None
or
let g x =
match x with
| (z : ^T when ^T : (static member IsInfinity : ^T -> bool)) -> Some z
| _ -> None
Neither which work.
You cannot do this, as Petr said, statically resolved type parameters are resolved at compile time. They're actually a feature of the F# compiler rather than being a .NET feature, hence why this kind of information isn't available at runtime.
If you wish to check this at runtime, you could use reflection.
let hasIsInfinity (x : 'a) =
typeof<'a>.GetMethod("IsInfinity", [|typeof<'a>|])
|> Option.ofObj
|> Option.exists (fun mi -> mi.ReturnType = typeof<bool> && mi.IsStatic)
This will check for a static method called IsInfinity with type sig: 'a -> bool

A shorter type constraint for F#

I'm practicing with F# and I've though of implementing a type-constrained compare function for F#. In C#, it's implemented by this
// in C#
static int cmp<T>(T x, T y) where T: System.IComparable<T>
{
return x.CompareTo(y);
}
However, in F#, the best way I've come up is this.
(* in F# *)
let cmp (x: 'a when 'a :> 'a System.IComparable) (y: 'a when 'a :> 'a System.IComparable)
= x.CompareTo(y)
I tried the one below but it didn't work
let cmp (x: 'a) (y: 'a) when 'a :> 'a System.IComparable
= x.CompareTo(y)
Is my working F# sample the shortest way or is there another?
Another (cleaner, IMO) way to implement this is by adding an explicit generic type parameter to the function, like this:
let cmp<'T when 'T :> System.IComparable<'T>> (x : 'T) (y : 'T) =
x.CompareTo y
Ok, found it. I was browsing through the examples in MSDN's F# type constraint and at the thrid from the last example, I found this
let inline add(value1 : ^T when ^T : (static member (+) : ^T * ^T -> ^T), value2: ^T) =
value1 + value2
I notice that constraint for ^T in value1 is used in value2, so I change my cmp function to this
let cmp (x: 'a when 'a :> 'a System.IComparable) (y: 'a) = x.CompareTo(y)
I ran through the fsi and got the same type signature
> let cmp (x: 'a when 'a :> 'a System.IComparable) (y: 'a) = x.CompareTo(y);;
val cmp : 'a -> 'a -> int when 'a :> System.IComparable<'a>
> let cmp (x: 'a when 'a :> 'a System.IComparable) (y: 'a when 'a :> 'a System.IComparable) = x.CompareTo(y);;
val cmp : 'a -> 'a -> int when 'a :> System.IComparable<'a>

Resources