Finalize part 10 of compiler series
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
1f00b6a3f8
commit
c9a7fbf6dd
|
@ -1,7 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Compiling a Functional Language Using C++, Part 11 - Polymorphic Data Types
|
title: Compiling a Functional Language Using C++, Part 11 - Polymorphic Data Types
|
||||||
date: 2020-03-28T20:10:35-07:00
|
date: 2020-04-14T19:05:42-07:00
|
||||||
draft: true
|
|
||||||
tags: ["C and C++", "Functional Languages", "Compilers"]
|
tags: ["C and C++", "Functional Languages", "Compilers"]
|
||||||
---
|
---
|
||||||
[In part 10]({{< relref "10_compiler_polymorphism.md" >}}), we managed to get our
|
[In part 10]({{< relref "10_compiler_polymorphism.md" >}}), we managed to get our
|
||||||
|
@ -99,7 +98,7 @@ Let's now enumerate all the possible forms that (mono)types can take in our syst
|
||||||
It is convenient to treat regular types (like \(\text{Bool}\)) as
|
It is convenient to treat regular types (like \(\text{Bool}\)) as
|
||||||
type constructors of arity 0 (that is, type constructors with kind \(*\)).
|
type constructors of arity 0 (that is, type constructors with kind \(*\)).
|
||||||
In effect, they take zero arguments and produce types (themselves).
|
In effect, they take zero arguments and produce types (themselves).
|
||||||
{{< /sidenote >}} such as \\(\\text{List} \; \\text{Int}\\) or \\(\\text{Bool}\\).
|
{{< /sidenote >}} such as \\(\\text{List} \\; \\text{Int}\\) or \\(\\text{Bool}\\).
|
||||||
3. A function from one type to another, like \\(\\text{List} \\; a \\rightarrow \\text{Int}\\).
|
3. A function from one type to another, like \\(\\text{List} \\; a \\rightarrow \\text{Int}\\).
|
||||||
|
|
||||||
Polytypes (type schemes) in our system can be all of the above, but may also include a "forall"
|
Polytypes (type schemes) in our system can be all of the above, but may also include a "forall"
|
||||||
|
@ -125,8 +124,8 @@ L_U & \rightarrow \epsilon
|
||||||
This grammar was actually too simple even for our monomorphically typed language!
|
This grammar was actually too simple even for our monomorphically typed language!
|
||||||
Since functions are not represented using a single uppercase variable, it wasn't possible for us
|
Since functions are not represented using a single uppercase variable, it wasn't possible for us
|
||||||
to define constructors that accept as arguments anything other than integers and user-defined
|
to define constructors that accept as arguments anything other than integers and user-defined
|
||||||
data types. Now, we also need to modify this grammar to allow for constructor applications (which can be nested!)
|
data types. Now, we also need to modify this grammar to allow for constructor applications (which can be nested).
|
||||||
To do so, we will define a new nonterminal, \\(Y\\), for types:
|
To do all of these things, we will define a new nonterminal, \\(Y\\), for types:
|
||||||
|
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
|
@ -176,7 +175,8 @@ L_Y & \rightarrow \epsilon
|
||||||
{{< /latex >}}
|
{{< /latex >}}
|
||||||
|
|
||||||
Finally, we update the rules for the data type declaration, as well as for a single
|
Finally, we update the rules for the data type declaration, as well as for a single
|
||||||
constructor:
|
constructor. In these new rules, we use \\(L\_T\\) to mean a list of type variables.
|
||||||
|
The rules are as follows:
|
||||||
|
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
|
@ -257,7 +257,7 @@ In the future, if this becomes an issue, we will likely move to unique
|
||||||
type identifiers.
|
type identifiers.
|
||||||
{{< /sidenote >}} Note also the more basic fact that we added arity
|
{{< /sidenote >}} Note also the more basic fact that we added arity
|
||||||
to our `type_base`,
|
to our `type_base`,
|
||||||
{{< sidenote "left" "base-arity-note" "since it may now be a type constructor instead." >}}
|
{{< sidenote "left" "base-arity-note" "since it may now be a type constructor instead of a plain type." >}}
|
||||||
You may be wondering, why did we add arity to base types, rather than data types?
|
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,
|
Although so far, our language can only create type constructors from data type definitions,
|
||||||
it's possible (or even likely) that we will have
|
it's possible (or even likely) that we will have
|
||||||
|
@ -271,7 +271,7 @@ to include the new case of `type_app`. The adjusted function looks as follows:
|
||||||
|
|
||||||
{{< codelines "C++" "compiler/11/type.cpp" 174 187 >}}
|
{{< codelines "C++" "compiler/11/type.cpp" 174 187 >}}
|
||||||
|
|
||||||
There another adjustment that we have to make to our type code. Recall
|
There is another adjustment that we have to make to our type code. Recall
|
||||||
that we had code that implemented substitutions: replacing free variables
|
that we had code that implemented substitutions: replacing free variables
|
||||||
with other types to properly implement our type schemes. There
|
with other types to properly implement our type schemes. There
|
||||||
was a bug in that code, which becomes much more apparent when the substitution
|
was a bug in that code, which becomes much more apparent when the substitution
|
||||||
|
@ -325,7 +325,7 @@ type variable to the final return type (which is something like `List a`),
|
||||||
in the order they occur.
|
in the order they occur.
|
||||||
2. When the variables have been gathered into a set, we iterate
|
2. When the variables have been gathered into a set, we iterate
|
||||||
over all constructors, and convert them into types by calling `to_type`
|
over all constructors, and convert them into types by calling `to_type`
|
||||||
on their arguments, and assemble the resulting argument types into a function.
|
on their arguments, then assembling the resulting argument types into a function.
|
||||||
This is not enough, however,
|
This is not enough, however,
|
||||||
{{< sidenote "right" "type-variables-note" "since constructors of types that accept type variables are polymorphic," >}}
|
{{< 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
|
This is also not enough because without generalization using "forall", we are risking using type variables
|
||||||
|
@ -335,10 +335,11 @@ wanted type constructors to be monomorphic (but generic, with type variables) we
|
||||||
instnatiate fresh type variables for every user-defined type variable, and substitute them appropriately.
|
instnatiate fresh type variables for every user-defined type variable, and substitute them appropriately.
|
||||||
{{< /sidenote >}}
|
{{< /sidenote >}}
|
||||||
as we have discussed above with \\(\\text{Nil}\\) and \\(\\text{Cons}\\).
|
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
|
To accomodate for this, we also add all type variables to the "forall" quantifier
|
||||||
of a new type scheme, whose monotype is the result of our calls to `to_type`.
|
of a new type scheme, whose monotype is our newly assembled function type. This
|
||||||
|
type scheme is what we store as the type of the constructor.
|
||||||
|
|
||||||
This is the last major change we have to perform. The rest is cleanup: we have switched
|
This was 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
|
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
|
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:
|
`ast_int` to create a reference to an existing integer type during typechecking:
|
||||||
|
|
Loading…
Reference in New Issue
Block a user