0
votes

Consider this code:

let f<'a when 'a:comparison> (x: 'a) = ()
let g x = f x
let h<'b> (x: 'b) = f x

The function f has a generic parameter constrained with comparison.
In function g, I just use f and let the compiler infer generic parameters, which it correctly does, resulting in g: 'a -> unit when 'a : comparison.

However, if I need to specify generic parameters explicitly, as in function h, the compiler fails to infer constraints for some reason, resulting in a red squiggly under x, saying "A type parameter is missing a constraint 'when 'b : comparison'."

This is especially annoying with functions that don't have non-generic parameters, i.e. let f<'a> = (), or whose non-generic parameters don't mention generic types, i.e. let f<'a> () = (), because I end up having to specify the constraints every single time I use such functions in another generic function:

let f<'a when 'a:comparison> () = ()
let g<'a> () = f<'a> () // Error: A type parameter is missing a constraint 
let h () = f<'a> () // h gets inferred as unit -> unit, losing generic parameter
let k<'a when 'a:comparison> () = f<'a> () // The only working way

Is there a trick to have the compiler infer the constraints for me?

2
I don't think there is a trick you could use - ideally, you would not need to make the type parameters of functions explicit because the compiler should be able to figure them out - or do you have some reason for requiring them?Tomas Petricek
The compiler can only figure out types that are used either in parameters or in result. I need a function that acts on types, not on values. This is used in conjunction with static constraints, similar to how generic zero works.Fyodor Soikin

2 Answers

2
votes

Remove g from your second set of functions and h generates the warning:

This construct causes code to be less generic than indicated by the type annotations. The type variable 'a has been constrained to be type 'System.IComparable'.

So, it seems the error in g is preventing a full type-check of h.

1
votes

As long as you only annotate the type parameters after the arguments and/or the return type, and not the function name, the type inference seems to work.

To follow from your first example:

let f<'a when 'a:comparison> (x: 'a) = ()

let i (x: 'b) = f x 
// val i : x:'b -> unit when 'b : comparison

It also works if the arguments have a generic type and/or an additional type constraint themselves:

let j (x: 'c list) = f x.Head
// val j : x:'c list -> unit when 'c : comparison

let k (x: 'd when 'd:unmanaged) = f x
// val k : x:'d -> unit when 'd : unmanaged and 'd : comparison

You should be able to code in this way just fine, since specifying the type parameter in the function name is unnecessary.

Even if you use a constrained type more than once, you only need to describe it the first time:

let l (x: 'e when 'e:unmanaged) (y : 'e) = f x
// val l : x:'e -> y:'e -> unit when 'e : unmanaged and 'e : comparison

All the same tricks works with the return type:

let f'<'a when 'a:comparison> () = (new obj ()) :?> 'a

let g' () : 'b when 'b : unmanaged = f' ()
// val g' : unit -> 'b when 'b : unmanaged and 'b : comparison

So the only pain point left is a generic function that has neither a generic input parameter nor a generic return type.