I notice a very strange behavior with type inference mechanism in ReasonML. I have a record containing identify function. When I use the record instance directly, the compiler doesn't complain. But when I pass the record to another function and try to invoke identity function, then type inference complains:
type idRecord('a) = {
// idFn can take any type.
idFn: 'a => 'a
};
let myRecord: idRecord('a) = {
idFn: anyVal => anyVal
};
// WORKS ABSOLUTELY FINE
let x1 = myRecord.idFn(10);
let x2 = myRecord.idFn("Something");
let runProgram = (program: idRecord('a)) => {
let _y1 = program.idFn(10);
// BOOM: ERROR
// This expression has type string but an expression was expected of type int
let _y2 = program.idFn("Something");
}
runProgram(myRecord);
The error is:
This expression has type string but an expression was expected of type int
What do I need to make type inference happy to accept any type of argument?
I'm no expert on the type inference algorithm, but to me it seems odd that it works in the first case since the type variable is defined on the record, not just for the function. Consider what would happen if you added another field to idRecord
of type 'a
:
type idRecord('a) = {
idFn: 'a => 'a,
value: 'a
};
I suspect it works due to a relaxation of the type inference rules that only works in some very limited conditions which does not include the record being a function argument.
In any case, the solution is simple: Remove the type variable from the record and universally quantify 'a
in the function type signature:
type idRecord = {
idFn: 'a. 'a => 'a
};
'a.
, which should be read "for all 'a
s", ensures that 'a
is fully polymorphic and will accept any type.