Nullable constraints are 'T (requires default constructor and struct and :> ValueType)
// Compiles
public void M(Nullable<(int, string)> foo) {
}
// Compiles
public void M<Foo>(Nullable<(int, Foo)> foo) {
}
// Errs: The type 'struct (int * string)' is not compatible with the type 'ValueType'
let m (foo: Nullable<struct(int * string)>) = Nullable.op_Implicit(null)
// No difference
let m (foo: Nullable<ValueTuple<int, string>>) = Nullable.op_Implicit(null)
// Errs: A generic construct requires that the type 'struct (int * 'Foo)' have a public default constructor
type T<'Foo>() =
let m (foo: Nullable<struct(int * 'Foo)>) = Nullable.op_Implicit(null)
I'm also not sure where Nullable's requires default constructor constraint comes from, in C# it's just constrained as follows <T> where T : struct
This means any type argument to nullable where said type doesn't have a default constructor would actually cause that last error.
Confirmed, no way to create * generic * structs in F# that will fit Nullable's constraints (and I assume any other type with the same constraints) if used in a generic function/method.
F# defined structs only seem to work for Nullable if all arguments are concrete.
It seems the compiler wants to inductively check the type before deciding if the type itself satisfies the default constructor constraint? Obviously it would fail if some parameters are still free. But that behavior doesn't make a lot of sense to me.
type [<Struct>] Foo<'bar>(bar: 'bar) = struct end
// Compiles
let m (foo: Nullable<Foo<int>>) = Nullable.op_Implicit(null)
// A generic construct requires that the type 'Foo<'bar>' have a public default constructor
let m (foo: Nullable<Foo<'bar>>) = Nullable.op_Implicit(null)
// C# type
// Compiles
let m (foo: Nullable<Memory<int>>) = Nullable.op_Implicit(null)
// Compiles
let m (foo: Nullable<Memory<'bar>>) = Nullable.op_Implicit(null)
I'll change the title...
This seems mostly like a bug to me, but I'd like @dsyme to chime in about this behavior to see if it's intended, since the introduction of Nullable Value Types (I'm calling them this now, instead of the legacy "Nullable Types" name) came quite a while back and there doesn't appear to be anything about this in the spec.
Yes, those should both be fixed AFAICS
This seems to be similar to https://github.com/dotnet/fsharp/issues/7618, generally speaking.
There's work to improve this here https://github.com/dotnet/fsharp/pull/10006. I've linked this bug there to make sure we capture all the test cases
Confirmed, no way to create * generic * structs in F# that will fit Nullable's constraints (and I assume any other type with the same constraints) if used in a generic function/method.
@NinoFloris #10006 completes this.
That said, when instantiating Nullable with generic structs and generic struct anonymous records beware that
Nullable forces a default value (e.g. a default constructor) on to it's type argument., so the struct tuple type must have a default value
This means each element type of the struct tuple/anon-record must have a default value.
Now, F# is fairly strict about when types have default values.
Prior to the fix in #10006 generic type variables were never considered to have default values for the purposes of this analysis.
Even after #10006 a type variable must still have an explicit annotation (or inferred constraint) that the type is either : struct or : not struct - it can't be neutral about this.
If : struct then a default constructor constraint : (new : unit -> 'T) is also required
If : not struct then a : null constraint is required.