I want to explicitly provide type parameters when I instantiate a generic record. To put in in another words, given a RecordType<'T1, 'T2, 'T3>
, I want create an instance of RecordType<'T1, 'T2, 'T3>
with some fixed 'T1
, 'T2
and 'T3
by specifying those generic parameters. Is there a way to do that in F#?
I see three cases where it's useful:
Instantiating a record when there's more than one generic type with the same name
Assume that we have following record definitions:
type SimpleGenericRecord<'T1, 'T2> = {
f1 : 'T1 -> 'T2
}
type SimpleGenericRecord<'T> = {
f1 : 'T -> 'T
}
I easily construct instances of SimpleGenericRecord<'T>
which was
defined last:
let record = {
f1 = fun (x: int) -> 0
}
let record1 = {
SimpleGenericRecord.f1 = fun (x: int) -> 0
}
Following attempts to create SimpleGenericRecord<int, int>
give
compile errors:
let record2 = {
SimpleGenericRecord<int, int>.f1 = fun (x: int) -> 0
}
let record3 = {
SimpleGenericRecord<_, _>.f1 = fun (x: int) -> 0
}
I know that having the same record name for two types might be not the best idea, nevertheless, I think that language should give me a way to use both types.
Documenting record types
F# reference says:
Don't use the DefaultValue attribute with record fields. A better approach is to define default instances of records with fields that are initialized to default values and then use a copy and update record expression to set any fields that differ from the default values.
Following that piece of advice, I would like to define default instances of records and, since they are part of a public API, document their types.
Helping in type inference
Generic parameters of a record type could be used to infer types of record's values.
Assume that I have:
type RecordWithSomeComplexType<'T> = {
t1 : int -> System.Collections.Generic.Dictionary<int, 'T> // some long type signature
}
and I want to instantiate it. If I don't provide any type annotations, record values will be as as general as possible, e.g.
let record4 = {
RecordWithSomeComplexType.t1 = failwith "Intentionally failing"
}
has type
int -> System.Collections.Generic.Dictionary<int, obj>
I can force a record to be of a certain type (e.g. RecordWithSomeComplexType<string>
), but in that case I need to write full type of certain value, e.g.
let failing = {
RecordWithSomeComplexType.t1 =
failwith "Intentionally failing" :> int -> System.Collections.Generic.Dictionary<int, string>
// I don't want to provide a full type of a value here
}
If compiler knew that I want to RecordWithSomeComplexType<string>
, it could infer value's signature.
You can add type annotation pretty much anywhere
let record2 : SimpleGenericRecord<_, _> = {
f1 = fun (x: int) -> 0
}
// alternative
let record2 =
({
f1 = fun (x: int) -> 0
} : SimpleGenericRecord<_, _>)
And for the longer case you can write an alias type to ease things
type Alias<'T> = int -> System.Collections.Generic.Dictionary<int, 'T>
let record4 = {
t1 = (failwith "Intentionally failing" : Alias<string>)
}
Note that record4 evaluation will raise the exception instantly because it isn't delayed
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With