diff --git a/code/compiler/11/examples/list.txt b/code/compiler/11/examples/list.txt
new file mode 100644
index 0000000..159bd1c
--- /dev/null
+++ b/code/compiler/11/examples/list.txt
@@ -0,0 +1,32 @@
+data List a = { Nil, Cons a (List a) }
+
+defn map f l = {
+ case l of {
+ Nil -> { Nil }
+ Cons x xs -> { Cons (f x) (map f xs) }
+ }
+}
+
+defn foldl f b l = {
+ case l of {
+ Nil -> { b }
+ Cons x xs -> { foldl f (f b x) xs }
+ }
+}
+
+defn foldr f b l = {
+ case l of {
+ Nil -> { b }
+ Cons x xs -> { f x (foldr f b xs) }
+ }
+}
+
+defn list = { Cons 1 (Cons 2 (Cons 3 (Cons 4 Nil))) }
+
+defn add x y = { x + y }
+defn sum l = { foldr add 0 l }
+
+defn skipAdd x y = { y + 1 }
+defn length l = { foldr skipAdd 0 l }
+
+defn main = { sum list + length list }
diff --git a/code/compiler/11/examples/pair.txt b/code/compiler/11/examples/pair.txt
new file mode 100644
index 0000000..3ed8362
--- /dev/null
+++ b/code/compiler/11/examples/pair.txt
@@ -0,0 +1,17 @@
+data Pair a b = { MkPair a b }
+
+defn fst p = {
+ case p of {
+ MkPair a b -> { a }
+ }
+}
+
+defn snd p = {
+ case p of {
+ MkPair a b -> { b }
+ }
+}
+
+defn pair = { MkPair 1 (MkPair 2 3) }
+
+defn main = { fst pair + snd (snd pair) }
diff --git a/content/blog/00_compiler_intro.md b/content/blog/00_compiler_intro.md
index 5ae24aa..683f129 100644
--- a/content/blog/00_compiler_intro.md
+++ b/content/blog/00_compiler_intro.md
@@ -142,3 +142,4 @@ Here are the posts that I've written so far for this series:
* [LLVM]({{< relref "08_compiler_llvm.md" >}})
* [Garbage Collection]({{< relref "09_compiler_garbage_collection.md" >}})
* [Polymorphism]({{< relref "10_compiler_polymorphism.md" >}})
+* [Polymorphic Data Types]({{< relref "11_compiler_polymorphic_data_types.md" >}})
diff --git a/content/blog/10_compiler_polymorphism.md b/content/blog/10_compiler_polymorphism.md
index 53d9070..1639fdd 100644
--- a/content/blog/10_compiler_polymorphism.md
+++ b/content/blog/10_compiler_polymorphism.md
@@ -777,6 +777,6 @@ While this is a major success, we are not yet done. Although our functions can n
have polymorphic types, the same cannot be said for our data types! We want to
have lists of integers __and__ lists of booleans, without having to duplicate any code!
While this also falls into the category of polymorphism, this post has already gotten very long,
-and we will return to it in the near future. Once we're done with that, I still intend
+and we will return to it in [part 11]({{< relref "11_compiler_polymorphic_data_types.md" >}}). Once we're done with that, I still intend
to go over `let/in` expressions, __lambda functions__, and __Input/Output__ together with
-__strings__. See you in these future posts!
+__strings__.
diff --git a/content/blog/11_compiler_polymorphic_data_types.md b/content/blog/11_compiler_polymorphic_data_types.md
index e6e203d..9d12ef4 100644
--- a/content/blog/11_compiler_polymorphic_data_types.md
+++ b/content/blog/11_compiler_polymorphic_data_types.md
@@ -185,24 +185,32 @@ D & \rightarrow \text{upperVar} \; L_Y \\
\end{aligned}
{{< /latex >}}
-Now that we have a grammar for all these things, we have to implement
-the corresponding data structures. We define a new family of structs,
-extending `parsed_type`, which represent types as they are
+Those are all the changes we have to make to our grammar. Let's now move on to implementing
+the corresponding data structures. We define a new family of structs, which represent types as they are
received from the parser. These differ from regular types in that they
-do not require that the types they represent are valid; validating
-types requires two passes, which is a luxury we do not have when
-parsing. We can define them as follows:
+do not necessarily represent valid types; validating types requires two passes, whereas parsing is
+done in a single pass. We can define our parsed types as follows:
{{< codeblock "C++" "compiler/11/parsed_type.hpp" >}}
-We define the conversion function `to_type`, which requires
-a set of type variables quantified in the given type, and
-the environment in which to look up the arities of various
-type constructors. The implementation is as follows:
+We define the conversion method `to_type`, which requires
+a set of type variables that are allowed to occur within a parsed
+type (which are the variables specified on the left of the `=`
+in the data type declaration syntax), and the environment in which to
+look up the arities of any type constructors. The implementation is as follows:
{{< codeblock "C++" "compiler/11/parsed_type.cpp" >}}
-With this definition in hand, we can now update the grammar in our Bison file.
+Note that this definition requires a new `type` subclass, `type_app`, which
+represents type application. Unlike `parsed_type_app`, it stores a pointer
+to the type constructor being applied, rather than its name. This
+helps validate the type (by making sure the parsed type's name refers to
+an existing type constructor), and lets us gather information like
+which constructors the resulting type has. We define this new type as follows:
+
+{{< codelines "C++" "compiler/11/type.hpp" 70 78 >}}
+
+With our new data structures in hand, we can now update the grammar in our Bison file.
First things first, we'll add the type parameters to the data type definition:
{{< codelines "plaintext" "compiler/11/parser.y" 127 130 >}}
@@ -211,10 +219,179 @@ Next, we add the new grammar rules we came up with:
{{< codelines "plaintext" "compiler/11/parser.y" 138 163 >}}
+Note in the above rules that even for `typeListElement`, which
+can never be applied to any arguments, we still attach a `parsed_type_app`
+as the semantic value. This is for consistency; it's easier to view
+all types in our system as applications to zero or more arguments,
+than to write coercions from non-applied types to types applied to zero
+arguments.
+
Finally, we define the types for these new rules at the top of the file:
{{< codelines "plaintext" "compiler/11/parser.y" 43 44 >}}
-{{< todo >}}
-Nullary is not the right word.
-{{< /todo >}}
+This concludes our work on the parser, but opens up a whole can of worms
+elsewhere. First of all, now that we introduced a new `type` subclass, we must
+ensure that type unification still works as intended. We therefore have
+to adjust the `type_mgr::unify` method:
+
+{{< codelines "C++" "compiler/11/type.cpp" 95 132 >}}
+
+In the above snippet, we add a new if-statement that checks whether or
+not both types being unified are type applications, and if so, unifies
+their constructors and arguments. We also extend our type equality check
+to ensure that both the names _and_ arities of types match
+{{< sidenote "right" "type-equality-note" "when they are compared for equality." >}}
+This is actually a pretty silly measure. Consider the following three
+propositions:
+1) types are only declared at the top-level scope.
+2) if a type is introduced, and another type with that name already exists, we throw an error.
+3) for name equality to be insufficient, we need to have two declared types
+with the same name. Given these propositions, it will not be possible for us to
+declare two types that would confuse the name equality check. However,
+in the near future, these propositions may not all hold: if we allow
+let/in
expressions to contain data type definitions,
+it will be possible to declare two types with the same name and arity
+(in different scopes), which would still confuse the check.
+In the future, if this becomes an issue, we will likely move to unique
+type identifiers.
+{{< /sidenote >}} Note also the more basic fact that we added arity
+to our `type_base`,
+{{< sidenote "left" "base-arity-note" "since it may now be a type constructor instead." >}}
+You may be wondering, why did we add arity to base types, rather than data types?
+Although so far, our language can only create type constructors from data type definitions,
+it's possible (or even likely) that we will have
+polymorphic built-in types, such as
+the IO monad.
+To prepare for this, we will allow our base types to be type constructors too.
+{{< /sidenote >}}
+
+Jut as we change `type_mgr::unify`, we need to change `type_mgr::find_free`
+to include the new case of `type_app`. The adjusted function looks as follows:
+
+{{< codelines "C++" "compiler/11/type.cpp" 174 187 >}}
+
+There another adjustment that we have to make to our type code. Recall
+that we had code that implemented substitutions: replacing free variables
+with other types to properly implement our type schemes. There
+was a bug in that code, which becomes much more apparent when the substitution
+system is put under more pressure. Specifically, the bug was in how type
+variables were handled.
+
+The old substitution code, when it found that a type
+variable had been bound to another type, always moved on to perform
+a substitution in that other type. This wasn't really a problem then, since
+any type variables that needed to be substituted were guaranteed to be
+free (that's why they were put into the "forall" quantifier). However, with our
+new system, we are using user-provided type variables (usually `a`, `b`, and so on),
+which have likely already been used by our compiler internally, and thus have
+been bound to something. That something is irrelevant to us: when we
+perform a substitution on a user-defined data type, we _know_ that _our_ `a` is
+free, and should be substitited. In short, precedence should be given to
+substituting type variables, rather than resolving them to what they are bound to.
+
+To make this adjustment possible, we need to make `substitute` a method of `type_manager`,
+since it will now require an awareness of existing type bindings. Additionally,
+this method will now perform its own type resolution, checking if a type variable
+needs to be substitited between each step. The whole code is as follows:
+
+{{< codelines "C++" "compiler/11/type.cpp" 134 165 >}}
+
+That's all for types. Definitions, though, need some work. First of all,
+we've changed our parser to feed our `constructor` type a vector of
+`parsed_type_ptr`, rather than `std::string`. We therefore have to update
+`constructor` to receive and store this new vector:
+
+{{< codelines "C++" "compiler/11/definition.hpp" 13 20 >}}
+
+Similarly, `definition_data` itself needs to accept the list of type
+variables it has:
+
+{{< codelines "C++" "compiler/11/definition.hpp" 54 70 >}}
+
+We then look at `definition_data::insert_constructors`, which converts
+`constructor` instances to actual constructor functions. The code,
+which is getting pretty complciated, is as follows:
+
+{{< codelines "C++" "compiler/11/definition.cpp" 64 92 >}}
+
+In the above snippet, we do the following things:
+
+1. We first create a set of type variables that can occur
+in this type's constructors (the same set that's used
+by the `to_type` method we saw earlier). While doing this, we ensure
+a type variable is not used twice (this is not allowed), and add each
+type variable to the final return type (which is something like `List a`),
+in the order they occur.
+2. When the variables have been gathered into a set, we iterate
+over all constructors, and convert them into types by calling `to_type`
+on their arguments, and assemble the resulting argument types into a function.
+This is not enough, however,
+{{< sidenote "right" "type-variables-note" "since constructors of types that accept type variables are polymorphic," >}}
+This is also not enough because without generalization using "forall", we are risking using type variables
+that have already been bound, or that will be bound. Even if a
has not yet been used by the typechecker,
+it will be once the type manager generates its first type variable, and things will go south. If we, for some reason,
+wanted type constructors to be monomorphic (but generic, with type variables) we'd need to internally
+instnatiate fresh type variables for every user-defined type variable, and substitute them appropriately.
+{{< /sidenote >}}
+as we have discussed above with \\(\\text{Nil}\\) and \\(\\text{Cons}\\).
+To accomodate for this, we also add all type variables we've used to the "forall" quantifier
+of a new type scheme, whose monotype is the result of our calls to `to_type`.
+
+This is the last major change we have to perform. The rest is cleanup: we have switched
+our system to dealing with type applications (sometimes with zero arguments), and we must
+bring the rest of the compiler up to speed with this change. For instance, we update
+`ast_int` to create a reference to an existing integer type during typechecking:
+
+{{< codelines "C++" "compiler/11/ast.cpp" 20 22 >}}
+
+Similarly, we update our code in `typecheck_program` to use
+type applications in the type for binary operations:
+
+{{< codelines "C++" "compiler/11/main.cpp" 31 37 >}}
+
+Finally, we update `ast_case` to unwrap type applications to get the needed constructor
+data from `type_data`. This has to be done in `ast_case::typecheck`, as follows:
+
+{{< codelines "C++" "compiler/11/ast.cpp" 163 168 >}}
+
+Additionally, a similar change needs to be made in `ast_case::compile`:
+
+{{< codelines "C++" "compiler/11/ast.cpp" 174 175 >}}
+
+That should be all! Let's try an example:
+
+{{< rawblock "compiler/11/examples/works3.txt" >}}
+
+The output:
+
+```
+Result: 6
+```
+
+Yay! Not only were we able to define a list of any type, but our `length` function correctly
+determined the lengths of two lists of different types! Let's try an example with the
+classic [`fold` functions](http://learnyouahaskell.com/higher-order-functions#folds):
+
+{{< rawblock "compiler/11/examples/list.txt" >}}
+
+We expect the sum of the list `[1,2,3,4]` to be `10`, and its length to be `4`, so the sum
+of the two should be `14`. And indeed, our program agrees:
+
+```
+Result: 14
+```
+
+Let's do one more example, to test types that take more than one type parameter:
+
+{{< rawblock "compiler/11/examples/pair.txt" >}}
+
+Once again, the compiled program gives the expected result:
+
+```
+Result: 4
+```
+
+This looks good! We have added support for polymorphic data types to our compiler.
+We are now free to move on to `let/in` expressions, __lambda functions__, and __Input/Output__,
+as promised! I'll see you then!