3.1 KiB
title | date | draft | tags | |||
---|---|---|---|---|---|---|
Compiling a Functional Language Using C++, Part 11 - Polymorphic Data Types | 2020-03-28T20:10:35-07:00 | true |
|
[In part 10]({{< relref "10_compiler_polymorphism.md" >}}), we managed to get our compiler to accept functions that were polymorphically typed. However, a piece of the puzzle is still missing: while our functions can handle values of different types, the same cannot be said for our data types. This means that we cannot construct data structures that can contain arbitrary types. While we can define and use a list of integers, if we want to also have a list of booleans, we must copy all of our constructors and define a new data type. Worse, not only do we have to duplicate the constructors, but also all the functions that operate on the list. As far as our compiler is concerned, a list of integers and a list of booleans are entirely different beasts, and cannot be operated on by the same code.
To make polymorphic data types possible, we must extend our language (and type system) a little. We will now allow for something like this:
data List a = { Nil, Cons a List }
In the above snippet, we are no longer declaring a single type, but a collection
of related types, parameterized by a type a
. Any type can take the place
of a
to get a list containing that type of element.
Then, List Int
is a type,
as is List Bool
and List (List Int)
. The constructors in the snippet also
get polymorphic types:
{{< latex >}} \text{Nil} : \forall a ; . ; \text{List} ; a \ \text{Cons} : \forall a ; . ; a \rightarrow \text{List} ; a \rightarrow \text{List} ; a {{< /latex >}}
When you call Cons
, the type of the resulting list varies with the type of element
you pass in. The empty list Nil
is a valid list of any type, since, well, it's
empty.
Let's talk about List
itself, now. I suggest that we ponder the following table:
\(\text{List}\) | \(\text{Cons}\) |
---|---|
\(\text{List}\) is not a type; it must be followed up with arguments, like \(\text{List} \; \text{Int}\). | \(\text{Cons}\) is not a list; it must be followed up with arguments, like \(\text{Cons} \; 3 \; \text{Nil}\). |
\(\text{List} \; \text{Int}\) is in its simplest form. | \(\text{Cons} \; 3 \; \text{Nil}\) is in its simplest form. |
\(\text{List} \; \text{Int}\) is a type. | \(\text{Cons} \; 3 \; \text{Nil}\) is a value of type \(\text{List} \; \text{Int}\). |
I hope that the similarities are quite striking. I claim that
List
is quite similar to a constructor Cons
, except that it occurs
in a different context: whereas Cons
is a way to create values,
List
is a way to create types. Indeed, while we call Cons
a constructor,
it's typicall to call List
a type constructor.
We know that Cons
is a function which
assigns to values (like 3
and Nil
) other values (like Cons 3 Nil
, or [3]
for
short). In a similar manner, List
can be thought of as a function
that assigns to types (like Int
) other types (like List Int
). We can
even claim that it has a type:
{{< latex >}} \text{List} : \text{Type} \rightarrow \text{Type} {{< /latex >}}