Reputation: 5528
I'm getting a signature mismatch in ReasonML between a type I defined which resolves to type mutationFunctionType = (~id: UUID.t, ~classroomId: UUID.t, unit) => unit;
and 'a
which I'm expecting. (seen below).
[1] Signature mismatch:
[1] ...
[1] Values do not match:
[1] let callMutationWithApollo:
[1] ApolloMutation.apolloMutationType(Config.t) => mutationFunctionType
[1] is not included in
[1] let callMutationWithApollo:
[1] ApolloMutation.apolloMutationType(Config.t) => 'a
I'm curious about why I'm receiving this error because I thought the polymorphic type `a could be treated as basically an any type.
Thanks
Upvotes: 2
Views: 137
Reputation: 35280
I'm curious about why I'm receiving this error because I thought the polymorphic type `a could be treated as basically any type.
Yep, you're right, 'a
means anything. But what it really means to be anything? The type of a value defines in which contexts this value could be used. For example, if your value has type int
then it could be used in any place where int
or ''a
are expected. Saying that your function is having type 'a
means that your function could be used instead of an int
a unit
or instead of any other function. Really, the type 'a` means that this value could be used instead of any other value.
Saying it differently the type of a function in your signature is your contract. And you're trying to over obligate yourself by saying that your function can fit anywhere. And the type checker is saying that you're not right - your function is only fittable in a very particular context
ApolloMutation.apolloMutationType(Config.t) => mutationFunctionType
It is not even polymorphic (i.e., it is not possible to fit your functions in more than one type - it is monomorphic, i.e., having only one type).
There is, however, one particular context, where using 'a
means "I don't care, pick whatever type you want", this is when you're specifying a type constraint, i.e., when you're annotating a parameter of a function or a variable in your let binding, e.g., here 'a
let sum = (x, y): 'a => x + y;
means, "whatever type", despite the fact that it is int
and only int
.
When you provide an annotation (aka type constraint) it is added by the typecheker as an additional constraint to the type equation, for example,
let pair: ('a, 'a) => ('a, 'a) = (x, y) => (x, y);
Here, the function pair
is constrained to be a pair of two equal (unifiable1) types, but those types could be anything, it only matters that they are equal. If we wouldn't add this constraint then the type of the function will be ('a,'b) => ('a,'b)
which is more general.
1)In fact, they are not required to be equal, such constraint just says that x
and y
must be unified, usually, in case of monomorphic types it means, that they should be equal, e.g., int
is only unifiable with int
, but in case of polymoprhic type, especially with subtypes, the least upper bound will be inferred, but this is a completely different story.
Upvotes: 1
Reputation: 5108
Type polymorphism allow you to write functions generically in some types.
For instance the polymorphic identity has type 'a => 'a
.
It means it can be used at type int => int
or bool => bool
by instantiating 'a
.
However, this 'a
does not mean anything goes. It only works in one direction:
'a
can become anything, but anything cannot become 'a
.
So from ApolloMutation.apolloMutationType(Config.t) => 'a
you can indeed get
ApolloMutation.apolloMutationType(Config.t) => mutationFunctionType
but what you're doing is the opposite.
For instance you cannot turn an int => int
function into a int => 'a
function.
By the way, providing such a function of type int => 'a
or ApolloMutation.apolloMutationType(Config.t) => 'a
is not really possible unless it returns errors or does not terminate. So I suggest updating your signature if possible to explicitly mention mutationFunctionType
.
Upvotes: 4