Update "compiler: polymorphism" to new math delimiters
Signed-off-by: Danila Fedorin <danila.fedorin@gmail.com>
This commit is contained in:
parent
bee06b6731
commit
2fde7e5cf8
|
@ -47,7 +47,7 @@ some of our notation from the [typechecking]({{< relref "03_compiler_typecheckin
|
||||||
{{< /latex >}}
|
{{< /latex >}}
|
||||||
|
|
||||||
But using our rules so far, such a thing is impossible, since there is no way for
|
But using our rules so far, such a thing is impossible, since there is no way for
|
||||||
\\(\text{Int}\\) to be unified with \\(\text{Bool}\\). We need a more powerful
|
\(\text{Int}\) to be unified with \(\text{Bool}\). We need a more powerful
|
||||||
set of rules to describe our program's types.
|
set of rules to describe our program's types.
|
||||||
|
|
||||||
|
|
||||||
|
@ -72,23 +72,23 @@ Rule|Name and Description
|
||||||
\frac
|
\frac
|
||||||
{x:\sigma \in \Gamma}
|
{x:\sigma \in \Gamma}
|
||||||
{\Gamma \vdash x:\sigma}
|
{\Gamma \vdash x:\sigma}
|
||||||
{{< /latex >}}| __Var__: If the variable \\(x\\) is known to have some polymorphic type \\(\\sigma\\) then an expression consisting only of that variable is of that type.
|
{{< /latex >}}| __Var__: If the variable \(x\) is known to have some polymorphic type \(\sigma\) then an expression consisting only of that variable is of that type.
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
\frac
|
\frac
|
||||||
{\Gamma \vdash e_1 : \tau_1 \rightarrow \tau_2 \quad \Gamma \vdash e_2 : \tau_1}
|
{\Gamma \vdash e_1 : \tau_1 \rightarrow \tau_2 \quad \Gamma \vdash e_2 : \tau_1}
|
||||||
{\Gamma \vdash e_1 \; e_2 : \tau_2}
|
{\Gamma \vdash e_1 \; e_2 : \tau_2}
|
||||||
{{< /latex >}}| __App__: If an expression \\(e\_1\\), which is a function from monomorphic type \\(\\tau\_1\\) to another monomorphic type \\(\\tau\_2\\), is applied to an argument \\(e\_2\\) of type \\(\\tau\_1\\), then the result is of type \\(\\tau\_2\\).
|
{{< /latex >}}| __App__: If an expression \(e_1\), which is a function from monomorphic type \(\tau_1\) to another monomorphic type \(\tau_2\), is applied to an argument \(e_2\) of type \(\tau_1\), then the result is of type \(\tau_2\).
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
\frac
|
\frac
|
||||||
{\Gamma \vdash e : \tau \quad \text{matcht}(\tau, p_i) = b_i
|
{\Gamma \vdash e : \tau \quad \text{matcht}(\tau, p_i) = b_i
|
||||||
\quad \Gamma,b_i \vdash e_i : \tau_c}
|
\quad \Gamma,b_i \vdash e_i : \tau_c}
|
||||||
{\Gamma \vdash \text{case} \; e \; \text{of} \;
|
{\Gamma \vdash \text{case} \; e \; \text{of} \;
|
||||||
\{ (p_1,e_1) \ldots (p_n, e_n) \} : \tau_c }
|
\{ (p_1,e_1) \ldots (p_n, e_n) \} : \tau_c }
|
||||||
{{< /latex >}}| __Case__: This rule is not part of Hindley-Milner, and is specific to our language. If the expression being case-analyzed is of type \\(\\tau\\) and each branch \\((p\_i, e\_i)\\) is of the same type \\(\\tau\_c\\) when the pattern \\(p\_i\\) works with type \\(\\tau\\) producing extra bindings \\(b\_i\\), the whole case expression is of type \\(\\tau\_c\\).
|
{{< /latex >}}| __Case__: This rule is not part of Hindley-Milner, and is specific to our language. If the expression being case-analyzed is of type \(\tau\) and each branch \((p_i, e_i)\) is of the same type \(\tau_c\) when the pattern \(p_i\) works with type \(\tau\) producing extra bindings \(b_i\), the whole case expression is of type \(\tau_c\).
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
\frac{\Gamma \vdash e : \sigma' \quad \sigma' \sqsubseteq \sigma}
|
\frac{\Gamma \vdash e : \sigma' \quad \sigma' \sqsubseteq \sigma}
|
||||||
{\Gamma \vdash e : \sigma}
|
{\Gamma \vdash e : \sigma}
|
||||||
{{< /latex >}}| __Inst (New)__: If type \\(\\sigma\\) is an instantiation (or specialization) of type \\(\\sigma\'\\) then an expression of type \\(\\sigma\'\\) is also an expression of type \\(\\sigma\\).
|
{{< /latex >}}| __Inst (New)__: If type \(\sigma\) is an instantiation (or specialization) of type \(\sigma'\) then an expression of type \(\sigma'\) is also an expression of type \(\sigma\).
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
\frac
|
\frac
|
||||||
{\Gamma \vdash e : \sigma \quad \alpha \not \in \text{free}(\Gamma)}
|
{\Gamma \vdash e : \sigma \quad \alpha \not \in \text{free}(\Gamma)}
|
||||||
|
@ -96,29 +96,29 @@ Rule|Name and Description
|
||||||
{{< /latex >}}| __Gen (New)__: If an expression has a type with free variables, this rule allows us generalize it to allow all possible types to be used for these free variables.
|
{{< /latex >}}| __Gen (New)__: If an expression has a type with free variables, this rule allows us generalize it to allow all possible types to be used for these free variables.
|
||||||
|
|
||||||
Here, there is a distinction between different forms of types. First, there are
|
Here, there is a distinction between different forms of types. First, there are
|
||||||
monomorphic types, or __monotypes__, \\(\\tau\\), which are types such as \\(\\text{Int}\\),
|
monomorphic types, or __monotypes__, \(\tau\), which are types such as \(\text{Int}\),
|
||||||
\\(\\text{Int} \\rightarrow \\text{Bool}\\), \\(a \\rightarrow b\\)
|
\(\text{Int} \rightarrow \text{Bool}\), \(a \rightarrow b\)
|
||||||
and so on. These types are what we've been working with so far. Each of them
|
and so on. These types are what we've been working with so far. Each of them
|
||||||
represents one (hence, "mono-"), concrete type. This is obvious in the case
|
represents one (hence, "mono-"), concrete type. This is obvious in the case
|
||||||
of \\(\\text{Int}\\) and \\(\\text{Int} \\rightarrow \\text{Bool}\\), but
|
of \(\text{Int}\) and \(\text{Int} \rightarrow \text{Bool}\), but
|
||||||
for \\(a \\rightarrow b\\) things are slightly less clear. Does it really
|
for \(a \rightarrow b\) things are slightly less clear. Does it really
|
||||||
represent a single type, if we can put an arbtirary thing for \\(a\\)?
|
represent a single type, if we can put an arbtirary thing for \(a\)?
|
||||||
The answer is "yes"! Although \\(a\\) is not currently known, it stands
|
The answer is "yes"! Although \(a\) is not currently known, it stands
|
||||||
in place of another monotype, which is yet to be determined.
|
in place of another monotype, which is yet to be determined.
|
||||||
|
|
||||||
So, how do we represent polymorphic types, like that of \\(\\text{if}\\)?
|
So, how do we represent polymorphic types, like that of \(\text{if}\)?
|
||||||
We borrow some more notation from mathematics, and use the "forall" quantifier:
|
We borrow some more notation from mathematics, and use the "forall" quantifier:
|
||||||
|
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
\text{if} : \forall a \; . \; \text{Bool} \rightarrow a \rightarrow a \rightarrow a
|
\text{if} : \forall a \; . \; \text{Bool} \rightarrow a \rightarrow a \rightarrow a
|
||||||
{{< /latex >}}
|
{{< /latex >}}
|
||||||
|
|
||||||
This basically says, "the type of \\(\\text{if}\\) is \\(\\text{Bool} \\rightarrow a \\rightarrow a \\rightarrow a\\)
|
This basically says, "the type of \(\text{if}\) is \(\text{Bool} \rightarrow a \rightarrow a \rightarrow a\)
|
||||||
for all possible \\(a\\)". This new expression using "forall" is what we call a type scheme, or a polytype \\(\\sigma\\).
|
for all possible \(a\)". This new expression using "forall" is what we call a type scheme, or a polytype \(\sigma\).
|
||||||
For simplicity, we only allow "forall" to be at the front of a polytype. That is, expressions like
|
For simplicity, we only allow "forall" to be at the front of a polytype. That is, expressions like
|
||||||
\\(a \\rightarrow \\forall b \\; . \\; b \\rightarrow b\\) are not valid polytypes as far as we're concerned.
|
\(a \rightarrow \forall b \; . \; b \rightarrow b\) are not valid polytypes as far as we're concerned.
|
||||||
|
|
||||||
It's key to observe that only some of the typing rules in the above table use polytypes (\\(\\sigma\\)). Whereas expressions
|
It's key to observe that only some of the typing rules in the above table use polytypes (\(\sigma\)). Whereas expressions
|
||||||
consisting of a single variable can be polymorphically typed, this is not true for function applications and case expressions.
|
consisting of a single variable can be polymorphically typed, this is not true for function applications and case expressions.
|
||||||
In fact, according to our rules, there is no way to introduce a polytype anywhere into our system!
|
In fact, according to our rules, there is no way to introduce a polytype anywhere into our system!
|
||||||
|
|
||||||
|
@ -127,8 +127,8 @@ this is called __Let-Polymorphism__, which means that only in `let`/`in` express
|
||||||
be given a polymorphic type. We, on the other hand, do not (yet) have `let`/`in` expressions, so our polymorphism
|
be given a polymorphic type. We, on the other hand, do not (yet) have `let`/`in` expressions, so our polymorphism
|
||||||
is further limited: only functions (and data type constructors) can be polymorphically typed.
|
is further limited: only functions (and data type constructors) can be polymorphically typed.
|
||||||
|
|
||||||
Let's talk about what __Inst__ does, and what "\\(\\sqsubseteq\\)" means.
|
Let's talk about what __Inst__ does, and what "\(\sqsubseteq\)" means.
|
||||||
First, why don't we go ahead and write the formal inference rule for \\(\\sqsubseteq\\):
|
First, why don't we go ahead and write the formal inference rule for \(\sqsubseteq\):
|
||||||
|
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
\frac
|
\frac
|
||||||
|
@ -137,21 +137,21 @@ First, why don't we go ahead and write the formal inference rule for \\(\\sqsubs
|
||||||
{{< /latex >}}
|
{{< /latex >}}
|
||||||
|
|
||||||
In my opinion, this is one of the more confusing inference rules we have to deal with in Hindley-Milner.
|
In my opinion, this is one of the more confusing inference rules we have to deal with in Hindley-Milner.
|
||||||
In principle, though, it's not too hard to understand. \\(\\sigma\' \\sqsubseteq \\sigma\\) says "\\(\\sigma\'\\)
|
In principle, though, it's not too hard to understand. \(\sigma' \sqsubseteq \sigma\) says "\(\sigma'\)
|
||||||
is more general than \\(\\sigma\\)". Alternatively, we can interpret it as "\\(\\sigma\\) is an instance of \\(\\sigma\'\\)".
|
is more general than \(\sigma\)". Alternatively, we can interpret it as "\(\sigma\) is an instance of \(\sigma'\)".
|
||||||
|
|
||||||
What does it mean for one polytype to be more general than another? Intuitively, we might say that \\(\forall a \\; . \\; a \\rightarrow a\\) is
|
What does it mean for one polytype to be more general than another? Intuitively, we might say that \(\forall a \; . \; a \rightarrow a\) is
|
||||||
more general than \\(\\text{Int} \\rightarrow \\text{Int}\\), because the former type can represent the latter, and more. Formally,
|
more general than \(\text{Int} \rightarrow \text{Int}\), because the former type can represent the latter, and more. Formally,
|
||||||
we define this in terms of __substitutions__. A substitution \\(\\{\\alpha \\mapsto \\tau \\}\\) replaces a variable
|
we define this in terms of __substitutions__. A substitution \(\{\alpha \mapsto \tau \}\) replaces a variable
|
||||||
\\(\\alpha\\) with a monotype \\(\\tau\\). If we can use a substitution to convert one type into another, then the first
|
\(\alpha\) with a monotype \(\tau\). If we can use a substitution to convert one type into another, then the first
|
||||||
type (the one on which the substitution was performed) is more general than the resulting type. In our previous example,
|
type (the one on which the substitution was performed) is more general than the resulting type. In our previous example,
|
||||||
since we can apply the substitution \\(\\{a \\mapsto \\text{Int}\\}\\) to get \\(\\text{Int} \\rightarrow \\text{Int}\\)
|
since we can apply the substitution \(\{a \mapsto \text{Int}\}\) to get \(\text{Int} \rightarrow \text{Int}\)
|
||||||
from \\(\\forall a \\; . \\; a \\rightarrow a\\), the latter type is more general; using our notation,
|
from \(\forall a \; . \; a \rightarrow a\), the latter type is more general; using our notation,
|
||||||
\\(\\forall a \\; . \\; a \\rightarrow a \\sqsubseteq \\text{Int} \\rightarrow \\text{Int}\\).
|
\(\forall a \; . \; a \rightarrow a \sqsubseteq \text{Int} \rightarrow \text{Int}\).
|
||||||
|
|
||||||
That's pretty much all that the rule says, really. But what about the whole thing with \\(\\beta\\) and \\(\\text{free}\\)? The reason
|
That's pretty much all that the rule says, really. But what about the whole thing with \(\beta\) and \(\text{free}\)? The reason
|
||||||
for that part of the rule is that, in principle, we can substitute polytypes into polytypes. However, we can't
|
for that part of the rule is that, in principle, we can substitute polytypes into polytypes. However, we can't
|
||||||
do this using \\(\\{ \\alpha \\mapsto \\sigma \\}\\). Consider, for example:
|
do this using \(\{ \alpha \mapsto \sigma \}\). Consider, for example:
|
||||||
|
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
\{ a \mapsto \forall b \; . \; b \rightarrow b \} \; \text{Bool} \rightarrow a \rightarrow a \stackrel{?}{=}
|
\{ a \mapsto \forall b \; . \; b \rightarrow b \} \; \text{Bool} \rightarrow a \rightarrow a \stackrel{?}{=}
|
||||||
|
@ -159,9 +159,9 @@ do this using \\(\\{ \\alpha \\mapsto \\sigma \\}\\). Consider, for example:
|
||||||
{{< /latex >}}
|
{{< /latex >}}
|
||||||
|
|
||||||
Hmm, this is not good. Didn't we agree that we only want quantifiers at the front of our types? Instead, to make that substitution,
|
Hmm, this is not good. Didn't we agree that we only want quantifiers at the front of our types? Instead, to make that substitution,
|
||||||
we only substitute the monotype \\(b \\rightarrow b\\), and then add the \\(\\forall b\\) at the front. But
|
we only substitute the monotype \(b \rightarrow b\), and then add the \(\forall b\) at the front. But
|
||||||
to do this, we must make sure that \\(b\\) doesn't occur anywhere in the original type
|
to do this, we must make sure that \(b\) doesn't occur anywhere in the original type
|
||||||
\\(\forall a \\; . \\; \\text{Bool} \\rightarrow a \\rightarrow a\\) (otherwise we can accidentally generalize
|
\(\forall a \; . \; \text{Bool} \rightarrow a \rightarrow a\) (otherwise we can accidentally generalize
|
||||||
too much). So then, our concrete inference rule is as follows:
|
too much). So then, our concrete inference rule is as follows:
|
||||||
|
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
|
@ -176,9 +176,9 @@ too much). So then, our concrete inference rule is as follows:
|
||||||
{{< /latex >}}
|
{{< /latex >}}
|
||||||
|
|
||||||
In the above rule we:
|
In the above rule we:
|
||||||
1. Replaced \\(a\\) with \\(b \\rightarrow b\\), getting rid of \\(a\\) in the quantifier.
|
1. Replaced \(a\) with \(b \rightarrow b\), getting rid of \(a\) in the quantifier.
|
||||||
2. Observed that \\(b\\) is not a free variable in the original type, and thus can be generalized.
|
2. Observed that \(b\) is not a free variable in the original type, and thus can be generalized.
|
||||||
3. Added the generalization for \\(b\\) to the front of the resulting type.
|
3. Added the generalization for \(b\) to the front of the resulting type.
|
||||||
|
|
||||||
Now, __Inst__ just allows us to perform specialization / substitution as many times
|
Now, __Inst__ just allows us to perform specialization / substitution as many times
|
||||||
as we need to get to the type we want.
|
as we need to get to the type we want.
|
||||||
|
@ -188,12 +188,12 @@ as we need to get to the type we want.
|
||||||
Alright, now we have all these rules. How does this change our typechecking algorithm?
|
Alright, now we have all these rules. How does this change our typechecking algorithm?
|
||||||
How about the following:
|
How about the following:
|
||||||
|
|
||||||
1. To every declared function, assign the type \\(a \\rightarrow ... \\rightarrow y \\rightarrow z\\),
|
1. To every declared function, assign the type \(a \rightarrow ... \rightarrow y \rightarrow z\),
|
||||||
where
|
where
|
||||||
{{< sidenote "right" "arguments-note" "\(a\) through \(y\) are the types of the arguments to the function," >}}
|
{{< sidenote "right" "arguments-note" "\(a\) through \(y\) are the types of the arguments to the function," >}}
|
||||||
Of course, there can be more or less than 25 arguments to any function. This is just a generalization;
|
Of course, there can be more or less than 25 arguments to any function. This is just a generalization;
|
||||||
we use as many input types as are needed.
|
we use as many input types as are needed.
|
||||||
{{< /sidenote >}} and \\(z\\) is the function's
|
{{< /sidenote >}} and \(z\) is the function's
|
||||||
return type.
|
return type.
|
||||||
2. We typecheck each declared function, using the __Var__, __Case__, __App__, and __Inst__ rules.
|
2. We typecheck each declared function, using the __Var__, __Case__, __App__, and __Inst__ rules.
|
||||||
3. Whatever type variables we don't fill in, we assume can be filled in with any type,
|
3. Whatever type variables we don't fill in, we assume can be filled in with any type,
|
||||||
|
@ -214,15 +214,15 @@ defn testTwo = { if True 0 1 }
|
||||||
|
|
||||||
If we go through and typecheck them top-to-bottom, the following happens:
|
If we go through and typecheck them top-to-bottom, the following happens:
|
||||||
|
|
||||||
1. We start by assuming \\(\\text{if} : a \\rightarrow b \\rightarrow c \\rightarrow d\\),
|
1. We start by assuming \(\text{if} : a \rightarrow b \rightarrow c \rightarrow d\),
|
||||||
\\(\\text{testOne} : e\\) and \\(\\text{testTwo} : f\\).
|
\(\text{testOne} : e\) and \(\text{testTwo} : f\).
|
||||||
2. We look at `if`. We infer the type of `c` to be \\(\\text{Bool}\\), and thus, \\(a = \\text{Bool}\\).
|
2. We look at `if`. We infer the type of `c` to be \(\text{Bool}\), and thus, \(a = \text{Bool}\).
|
||||||
We also infer that \\(b = c\\), since they occur in two branches of the same case expression.
|
We also infer that \(b = c\), since they occur in two branches of the same case expression.
|
||||||
Finally, we infer that \\(c = d\\), since whatever the case expression returns becomes the return
|
Finally, we infer that \(c = d\), since whatever the case expression returns becomes the return
|
||||||
value of the function. Thus, we come out knowing that \\(\\text{if} : \\text{Bool} \\rightarrow b
|
value of the function. Thus, we come out knowing that \(\text{if} : \text{Bool} \rightarrow b
|
||||||
\\rightarrow b \\rightarrow b\\).
|
\rightarrow b \rightarrow b\).
|
||||||
3. Now, since we never figured out \\(b\\), we use __Gen__: \\(\\text{if} : \\forall b \\; . \\;
|
3. Now, since we never figured out \(b\), we use __Gen__: \(\text{if} : \forall b \; . \;
|
||||||
\\text{Bool} \\rightarrow b \\rightarrow b \\rightarrow b\\). Like we'd want, `if` works with
|
\text{Bool} \rightarrow b \rightarrow b \rightarrow b\). Like we'd want, `if` works with
|
||||||
all types, as long as both its inputs are of the same type.
|
all types, as long as both its inputs are of the same type.
|
||||||
4. When we typecheck the body of `testOne`, we use __Inst__ to turn the above type for `if`
|
4. When we typecheck the body of `testOne`, we use __Inst__ to turn the above type for `if`
|
||||||
into a single, monomorphic instance. Then, type inference proceeds as before, and all is well.
|
into a single, monomorphic instance. Then, type inference proceeds as before, and all is well.
|
||||||
|
@ -231,15 +231,15 @@ and all is well again.
|
||||||
|
|
||||||
So far, so good. But what if we started from the bottom, and went to the top?
|
So far, so good. But what if we started from the bottom, and went to the top?
|
||||||
|
|
||||||
1. We start by assuming \\(\\text{if} : a \\rightarrow b \\rightarrow c \\rightarrow d\\),
|
1. We start by assuming \(\text{if} : a \rightarrow b \rightarrow c \rightarrow d\),
|
||||||
\\(\\text{testOne} : e\\) and \\(\\text{testTwo} : f\\).
|
\(\text{testOne} : e\) and \(\text{testTwo} : f\).
|
||||||
2. We look at `testTwo`. We infer that \\(a = \\text{Bool}\\) (since
|
2. We look at `testTwo`. We infer that \(a = \text{Bool}\) (since
|
||||||
we pass in `True` to `if`). We also infer that \\(b = \\text{Int}\\), and that \\(c = \\text{Int}\\).
|
we pass in `True` to `if`). We also infer that \(b = \text{Int}\), and that \(c = \text{Int}\).
|
||||||
Not yet sure of the return type of `if`, this is where we stop. We are left with
|
Not yet sure of the return type of `if`, this is where we stop. We are left with
|
||||||
the knowledge that \\(f = d\\) (because the return type of `if` is the return type of `testTwo`),
|
the knowledge that \(f = d\) (because the return type of `if` is the return type of `testTwo`),
|
||||||
but that's about it. Since \\(f\\) is no longer free, we don't generalize, and conclude that \\(\text{testTwo} : f\\).
|
but that's about it. Since \(f\) is no longer free, we don't generalize, and conclude that \(\text{testTwo} : f\).
|
||||||
3. We look at `testOne`. We infer that \\(a = \\text{Bool}\\) (already known). We also infer
|
3. We look at `testOne`. We infer that \(a = \text{Bool}\) (already known). We also infer
|
||||||
that \\(b = \\text{Bool}\\), and that \\(c = \\text{Bool}\\). But wait a minute! This is not right.
|
that \(b = \text{Bool}\), and that \(c = \text{Bool}\). But wait a minute! This is not right.
|
||||||
We are back to where we started, with a unification error!
|
We are back to where we started, with a unification error!
|
||||||
|
|
||||||
What went wrong? I claim that we typechecked the functions that _used_ `if` before we typechecked `if` itself,
|
What went wrong? I claim that we typechecked the functions that _used_ `if` before we typechecked `if` itself,
|
||||||
|
@ -257,11 +257,11 @@ A transitive closure of a vertex in a graph is the list of all vertices reachabl
|
||||||
from that original vertex. Check out the <a href="https://en.wikipedia.org/wiki/Transitive_closure#In_graph_theory">
|
from that original vertex. Check out the <a href="https://en.wikipedia.org/wiki/Transitive_closure#In_graph_theory">
|
||||||
Wikipedia page on this</a>.
|
Wikipedia page on this</a>.
|
||||||
{{< /sidenote >}}
|
{{< /sidenote >}}
|
||||||
of the function dependencies. We define a function \\(f\\) to be dependent on another function \\(g\\)
|
of the function dependencies. We define a function \(f\) to be dependent on another function \(g\)
|
||||||
if \\(f\\) calls \\(g\\). The transitive closure will help us find functions that are related indirectly.
|
if \(f\) calls \(g\). The transitive closure will help us find functions that are related indirectly.
|
||||||
For instance, if \\(g\\) also depends on \\(h\\), then the transitive closure of \\(f\\) will
|
For instance, if \(g\) also depends on \(h\), then the transitive closure of \(f\) will
|
||||||
include \\(h\\), even if \\(f\\) directly doesn't use \\(h\\).
|
include \(h\), even if \(f\) directly doesn't use \(h\).
|
||||||
2. We isolate groups of mutually dependent functions. If \\(f\\) depends on \\(g\\) and \\(g\\) depends \\(f\\),
|
2. We isolate groups of mutually dependent functions. If \(f\) depends on \(g\) and \(g\) depends \(f\),
|
||||||
they are placed in one group. We then construct a dependency graph __of these groups__.
|
they are placed in one group. We then construct a dependency graph __of these groups__.
|
||||||
3. We compute a topological order of the group graph. This helps us typecheck the dependencies
|
3. We compute a topological order of the group graph. This helps us typecheck the dependencies
|
||||||
of functions before checking the functions themselves. In our specific case, this would ensure
|
of functions before checking the functions themselves. In our specific case, this would ensure
|
||||||
|
@ -271,7 +271,7 @@ in a group.
|
||||||
4. We typecheck the function groups, and functions within them, following the above topological order.
|
4. We typecheck the function groups, and functions within them, following the above topological order.
|
||||||
|
|
||||||
To find the transitive closure of a graph, we can use [Warshall's Algorithm](https://cs.winona.edu/lin/cs440/ch08-2.pdf).
|
To find the transitive closure of a graph, we can use [Warshall's Algorithm](https://cs.winona.edu/lin/cs440/ch08-2.pdf).
|
||||||
This algorithm, with complexity \\(O(|V|^3)\\), goes as follows:
|
This algorithm, with complexity \(O(|V|^3)\), goes as follows:
|
||||||
{{< latex >}}
|
{{< latex >}}
|
||||||
\begin{aligned}
|
\begin{aligned}
|
||||||
& A, R^{(i)} \in \mathbb{B}^{n \times n} \\
|
& A, R^{(i)} \in \mathbb{B}^{n \times n} \\
|
||||||
|
@ -285,12 +285,12 @@ This algorithm, with complexity \\(O(|V|^3)\\), goes as follows:
|
||||||
\end{aligned}
|
\end{aligned}
|
||||||
{{< /latex >}}
|
{{< /latex >}}
|
||||||
|
|
||||||
In the above notation, \\(R^{(i)}\\) is the \\(i\\)th matrix \\(R\\), and \\(A\\) is the adjacency
|
In the above notation, \(R^{(i)}\) is the \(i\)th matrix \(R\), and \(A\) is the adjacency
|
||||||
matrix of the graph in question. All matrices in the algorithm are from \\(\\mathbb{B}^{n \times n}\\),
|
matrix of the graph in question. All matrices in the algorithm are from \(\mathbb{B}^{n \times n}\),
|
||||||
the set of \\(n\\) by \\(n\\) boolean matrices. Once this algorithm is complete, we get as output a
|
the set of \(n\) by \(n\) boolean matrices. Once this algorithm is complete, we get as output a
|
||||||
transitive closure adjacency matrix \\(R^{(n)}\\). Mutually dependent functions will be pretty easy to
|
transitive closure adjacency matrix \(R^{(n)}\). Mutually dependent functions will be pretty easy to
|
||||||
isolate from this matrix. If \\(R^{(n)}[i,j]\\) and \\(R^{(n)}[j,i]\\), then the functions represented by vertices
|
isolate from this matrix. If \(R^{(n)}[i,j]\) and \(R^{(n)}[j,i]\), then the functions represented by vertices
|
||||||
\\(i\\) and \\(j\\) depend on each other.
|
\(i\) and \(j\) depend on each other.
|
||||||
|
|
||||||
Once we've identified the groups, and
|
Once we've identified the groups, and
|
||||||
{{< sidenote "right" "group-graph-note" "constructed a group graph," >}}
|
{{< sidenote "right" "group-graph-note" "constructed a group graph," >}}
|
||||||
|
@ -596,7 +596,7 @@ a value of a non-existent type), but a mature compiler should prevent this from
|
||||||
On the other hand, here are the steps for function definitions:
|
On the other hand, here are the steps for function definitions:
|
||||||
|
|
||||||
1. Find the free variables of each function to create the ordered list of groups as described above.
|
1. Find the free variables of each function to create the ordered list of groups as described above.
|
||||||
2. Within each group, insert a general function type (like \\(a \\rightarrow b \\rightarrow c\\))
|
2. Within each group, insert a general function type (like \(a \rightarrow b \rightarrow c\))
|
||||||
into the environment for each function.
|
into the environment for each function.
|
||||||
3. Within each group (in the same pass) run typechecking
|
3. Within each group (in the same pass) run typechecking
|
||||||
(including polymorphism, using the rules as described above).
|
(including polymorphism, using the rules as described above).
|
||||||
|
@ -707,8 +707,8 @@ is also updated to use topological ordering:
|
||||||
|
|
||||||
The above code uses the yet-unexplained `generalize` method. What's going on?
|
The above code uses the yet-unexplained `generalize` method. What's going on?
|
||||||
|
|
||||||
Observe that the __Var__ rule of the Hindley-Milner type system says that a variable \\(x\\)
|
Observe that the __Var__ rule of the Hindley-Milner type system says that a variable \(x\)
|
||||||
can have a __polytype__ in the environment \\(\\Gamma\\). Our `type_ptr` can only represent monotypes,
|
can have a __polytype__ in the environment \(\Gamma\). Our `type_ptr` can only represent monotypes,
|
||||||
so we must change what `type_env` associates with names to a new struct for representing polytypes,
|
so we must change what `type_env` associates with names to a new struct for representing polytypes,
|
||||||
which we will call `type_scheme`. The `type_scheme` struct, just like the formal definition of
|
which we will call `type_scheme`. The `type_scheme` struct, just like the formal definition of
|
||||||
a polytype, contains zero or more "forall"-quantified type variables, followed by a monotype which
|
a polytype, contains zero or more "forall"-quantified type variables, followed by a monotype which
|
||||||
|
|
Loading…
Reference in New Issue
Block a user