Type Parameter
A value constructor can take some values parameters and then produce a new value. A type constructor can take types as parameters to produce a new type. It's similar to template in C++.
Intro
data Maybe a = Nothing | Just a
Because there's a type parameter (a
) involved, we call Maybe
a type constructor (not Maybe a
). It takes a type parameter (a
), and creates a type (Maybe a
).
In order to make a real type that a value can be part of, it has to have all its type parameters filled up.
when to use type parameter
Using type parameters is very beneficial, but only when using them makes sense. Usually we use them when our data type would work regardless of the type of the value it then holds inside it, or the type that's contained inside the data type's various value constructors isn't really that important for the type to work. For example:
- A
list
of stuff is a list of stuff and it doesn't matter what the type of that stuff is, it can still work. If we want to sum a list of numbers, we can specify later in the summing function that we specifically want a list of numbers. Maybe
represents an option of either having nothing or having one of something. It doesn't matter what the type of that something is.
If our type acts as some kind of box, it's good to use them. We're more interested in the external behavior (the box), instead of the content (contained types) inside.
So don't put type constraints into data declarations even if it seems to make sense, because you'll have to put them into the function type declarations either way.
Type parameter vs. type Variables
- Type parameter: in data type declarations, like
Maybe a
- Type Variables: in function type declarations, like
singleton :: forall a. a -> Array a
It seems no significant difference, except that the first a insingleton
declaration does not live in a type declaration.