F# type constraints and reflection - f#

Is there any way to determine whether a given type parameter satisfies the F# comparison constraint through reflection?
I would suspect not, since the expression
typedefof<Set<_>>.MakeGenericType [| typeof<System.Type> |]
appears to yield no errors. Still, I would like to hear some authoritative opinion on this.

Quoting from Don Syme's thorough post on equality and comparison constraints:
The constraint type : comparison holds if:
if the type is a named type, then the type definition doesn't have the NoComparison attribute; and
the type definition implements System.IComparable; and
any “comparison dependencies” of the type also satisfy tyi : comparison
The constraint 'T when 'T :> IComparable can be encoded in CIL and reflected upon, whereas neither is true of 'T when 'T : comparison.
Since the two constraints are not equivalent, marking comparable types with the IComparable constraint is a bit misleading since it would make it impossible to distinguish between the two using reflection.
There's a similar relationship between the equality constraint and IEquatable<_>.
EDIT
Jack's mention that the comparison constraint could be encoded in F# metadata prompted me to look at the metadata reader in PowerPack. It can be used to detect the constraint:
open Microsoft.FSharp.Metadata
let setEntity = FSharpAssembly.FSharpLibrary.GetEntity("Microsoft.FSharp.Collections.FSharpSet`1")
for typeArg in setEntity.GenericParameters do
printfn "%s - comparison=%b"
typeArg.Name
(typeArg.Constraints |> Seq.exists (fun c -> c.IsComparisonConstraint))
Here's a contrived example that shows the disparity between implementing IComparable and satisfying comparison:
type A() =
interface IComparable with
member __.CompareTo(_) = 0
[<NoComparison>]
type B() =
inherit A()
type C<'T when 'T : comparison>() = class end
type D<'T when 'T :> IComparable>() = class end
let c = C<B>() //ERROR
let d = D<B>() //OK

Related

"Invalid constraint: the type used for the constraint is sealed" is invalid

I'm wish to use F#'s type inference to infer the types of my code simply by the methods/functions that are called.
This CAN be achieved quite nicely by using extension methods of the form...
[<Extension>]
type AttributeExtensions =
[<Extension>]
static member ``BroadcastOffset``<'a, 'b when 'a :> ``BroadcastOffset``<'b>> (this: 'a) =
this.``
when I have code that reads
x.BroadcastOffset()
type inference will kick in and infers that x, must indeed be of type BroadcastOffset<'a>.
A small fly in the ointment though is F#'s own types for example, if I write
x.Value
F# will not infer its an Option<_>, thats understandable, but I can use the same extension trick to get there?
[<Extension>]
type AttributeExtensions =
[<Extension>]
static member Value<'a,'b when 'a :> Option<'b>> (this: 'a) =
this.Value
and that SHOULD in theory mean
x.Value()
should trigger the inference of x being an Option<_>
sadly this ISNT the case, because F# rejects the extension method as an invalid constraint because Option<_> is sealed and thus 'a can only have 1 solution.
True 'a CAN only have 1 solution but, in my book that does NOT make the constraint invalid, it is perfectly valid, just trivial to infer, and because of this overzelous rejection, I am frustrated in making "Value" trigger type inference.
any ideas how to get around it? (apart from changing the F# compiler myself).
(I can of course define a function 'Value' instead, but my intention is to use F# to infer types from "methods", and extension methods fit the requirement where simple functions don't...I have my reasons)
I got compile errors using your code and then factored out what I think you are after into a function, then used the function in the extension. This allowed me to call .Value() and it work out that it was of type 'a option which is what I think you were after?
open System.Runtime.CompilerServices
let v (x : 'a option) = x.Value
[<Extension>]
type AttributeExtensions =
[<Extension>]
static member Value(this) = v(this)
let myFunc o = o.Value()

Why are the inferred F# types on these two definitions different in regards to IComparable and comparison?

Working in F# and having trouble understanding the difference in the following two items:
type A<'k, 'v when 'v : comparison and 'k : comparison> =
{ Keys: Map<'v, 'k> } with
member this.length = Map.count this.Keys
module A =
let empty = { Keys = Map.empty }
type B<'k, 'v when 'v : comparison and 'k : comparison>(keys: Map<'v, 'k>) =
member __.length = Map.count keys
module B =
let empty = new B<'k, 'v>(Map.empty)
If I look at the inferred types of A.empty and B.empty, I get the following:
val empty : A<'a,'b> (requires comparison and comparison)
val empty : B<System.IComparable,System.IComparable>
Why are they different? Both 'k and 'v are both being assigned to an F# Map, and are being used in the same way. The only difference is the "new" keyword on B, but why would that change the types inferred for the Map?
In your B.empty case, you have a let-bound value where you introduce type parameters 'k and 'v on the right side without referring to them on the left side. The type inference mechanism then fails to automatically generalize empty and instead assigns a specific type to it.
If you try using it in different instantiations, you'll see the second one failing:
let t1 : B<int, string> = B.empty
let t2 : B<string, int> = B.empty // <- getting a type mismatch here.
This is due to a property of type inference mechanism called value restriction. This blog post has a good in-depth description of it, including the very case you're looking at right now, implementing empty for a generic container type.
In short - B.empty can't be generalized here because the expression on the right hand side doesn't produce a value that is easily identifiable as immutable, and as such the compiler errs on the side of caution and decides not to generalize the type of empty.
On the other hand, A.empty on the right hand side has an expression creating an instance of an immutable record, which is one of the rare scenarios that meet requirements for automatic generalization.
Some ways to avoid it:
Making empty a function:
let empty () = new B<'k, 'v>(Map.empty)
Putting explicit type arguments on it:
let empty<'k, 'v when 'v : comparison and 'k : comparison> = new B<'k, 'v>(Map.empty)

The parameter has been used in a way that constrains it to always be DerrivedType

Hi I just started programming in F# and am stuck on a type issue.
I have this function:
member private this.UpdateStats<'T when 'T :> StatisticsBase>(passed: bool, stats: 'T) =
//more stuff..
stats
I'm calling it like this:
this.UpdateStats<GroupStats>(true, GroupStats(Id = Guid.NewGuid()))
The compiler says:
The parameter has been used in a way that constrains it to always be GroupStats.
GroupStats inherit from StatisticsBase. What do I have to do to make the function usable for all entities that inherit from StatisticsBase?
types:
[<AllowNullLiteral>]
type StatisticsBase() =
member val Id = String.Empty with get,set
[<AllowNullLiteral>]
type GroupStats() =
inherit Stats()
StatisticsBase actually inherits from a C# type and is used in a repository, but I can reproduce the error with the code above
After much back and forth, we have been able to ascertain that your actual non-working code is like this (tip for the future: provide more information):
type SomeType() =
member this.M2() =
this.M<GroupStats>(true, GroupStats())
member private this.M<'T when 'T :> Stats>(x: bool, t: 'T) =
t
This code will indeed produce the described error. This is because F# type inference works top-to-bottom, left-to-right. But there is an exception: classes (and other mutually recusrive definition groups) get two passes of type inference - first signatures, then bodies.
When the compiler first comes across the body of M2, it determines that method M must return GroupStats. Later, when the compiler comes across the body of M, it sees that M's return value is the same as parameter t, which means that M must return 'T. But since the compiler already knows, from examining the body of M2, that M must return GroupStats, it follows that 'T must be GroupStats.
This doesn't happen if M is defined before M2: in this case, type inference will first encounter the body of M and correctly determine its return type to be 'T, which will then match up with the body of M2, and the will be no problem.
From the above, two solutions can be formulated: first, you could define M before M2. Second, you could just explicitly specify the return type of M:
member private this.M<'T when 'T :> Stats>(x: bool, t: 'T): 'T =
t
This way, its return type will become known after the first pass, and the problem disappears.

Order of type constraints in F#

This works in F#4.0:
type Something<'a, 'b when 'b :> seq<'b>>() =
This doesn't:
type Something<'b when 'b :> seq<'b>, 'a>() =
Unexpected symbol ',' in type name. Expected '>' or other token.
What's the reason that the order of the type constraint matter?
Because it is in the spec - the relevant part is this (from the start of section 5):
typar-defns:= < typar-defn, ..., typar-defn typar-constraints_opt>
the constraints need to go at the end.
In this typar-constraints must always start with when and can't appear anywhere else.
Type constraints and type arguments are two different things. In your example, 'a and 'b are two type arguments, and when 'b :> seq<'b> is the (only) constraint.
Now, the way you've written your first example it seems to suggest that the type argument definitions have something to do with the type constraints, but that's only in the appearance. Note this (working) code:
type Something<'b, 'a when 'b :> seq<'b>>() =
member this.A (a : 'a, b : 'b) =
()
First, you define all the type arguments. Only afterwards come the type constraints, and the constraint still applies to 'b, not 'a - while it looks a bit confusing, the constraint expression isn't 'a when 'b :> seq<'b>, it's just the when 'b :> seq<'b>.
This is actually pretty much the same as in C#, another .NET language:
public class Something<TA, TB> where TA: someConstraint where TB: someOtherConstraint
The constraints are more visually separate in C#, so people don't tend to make the mistake you made in F#.
Along with the above answers, the ordering allows the constraints to depend on multiple types. e.g.
type Something<'a, 'b when 'b :> seq<'a>>() =
Note:seq<'a> not seq<'b>

Type inference wants explicit upcast sometimes even when flexible type is used for interface constraints?

I had expected that flexible type would minimize the need of explicit upcast, but
type IB =
abstract B : int
type A() =
interface IB with
member this.B = 1
let a = A()
let test (x) = x
let aa = [|a; a|]
let case1 = aa |> Array.map (fun (x: #IB) -> test (x.B))
let case2 = Array.map (fun (x: #IB) -> test (x.B)) aa
Here, there are warnings (less generic than type annotation) on the last 2 lines. The compiler is able to compile case2, but fails at case1, why is that?
It feels odd that the more detailed the compiler can infer, the more code I need to write.
#kvb pointed out there is a simple fix, just to refactor the lambda function into a non inline version.
let fix (x: #IB) =
test x.B
let case1 = aa |> Array.map fix
let case2 = Array.map fix
This works well.
Type inference in F# flows from left to right, so it's not too surprising that piping can break or fix things. However, it's almost always the case that having more type information earlier is helpful, so this result is a bit surprising. The warnings are a subtle indicator that you're doing something wrong. Flexible types seem like they ought to be applicable in many tricky situations, but there are really only a few places where they help. Array.map takes a function of some type 'a -> 'b for some particular 'a and 'b (though there's a little bit of nuance here, since the "particular" types could be type variables), so having a "more generic" argument like #IB -> int is not especially helpful; the compiler will pick some particular subtype of IB during compilation - this is what the warnings are attempting to communicate.
As I said, the different results you see on the different lines are due to the fact that type inference in F# works left to right. In the first case, the information about the type of aa flows into the type checking of the remainder of the expression and so when compiling the lambda the compiler knows that the only possible subtype of IB that will work is A, but because interface implementations are always implicit this causes a compile time error because A doesn't have a publicly available member B. On the other hand, in the second case, when the compiler's trying to check Array.map it doesn't yet know the type of the array it will be applied to, but can check the call in any case because regardless of the subtype it will support the interface method IB.B (implicitly upcasting the argument from whatever subtype of IB it has to just IB). Then when this is applied to aa the compiler specializes the implementation to A, but because of the implicit upcast this still works okay. Intuitively, it seems like the compiler ought to have been able to have inserted the same implicit upcast in the former case, but I think this is probably just a surprising result of the inference algorithm and not an actual bug.
Perhaps more surprising, one possible fix is to just use a let-bound definition (for either the lambda or even as a type-restricted version of Array.map) and skip the flexible type:
let f (x:IB) = test (x.B)
let g (f:IB -> int) = Array.map f
let d' = aa |> g (fun x -> test (x.B))
let d'' = aa |> Array.map f
let c' = g (fun x -> test (x.B)) aa
let c'' = Array.map f aa
So what's happening here? It turns out the compiler does something called "Implicit Insertion of Flexibility for Uses of Functions and Members" (section 14.4.3 of the spec for F# 3.1) which does exactly what you want.
type IB =
abstract B : int
type A() =
interface IB with
member this.B = 1
let a = A()
let test (x) = x
let aa = [|a; a|]
Here the compiler knows that aa is an array of A so type annotation in lambda is not needed but you have to cast x to the interface type because in F# interfaces are implemented explicitely (http://msdn.microsoft.com/en-us/library/ms173157.aspx):
let d = aa |> Array.map (fun x -> test ((x:>IB).B))
Here the compiler at the moment of compiling lambda doesn't know what a type of x is, so you need a type annotation telling that x is implementation of IB interface so you can refer to x.B property directly:
let c = Array.map (fun (x: #IB) -> test (x.B)) aa

Resources