Update "compiler: typechecking" to new math delimiters
Signed-off-by: Danila Fedorin <danila.fedorin@gmail.com>
This commit is contained in:
		
							parent
							
								
									6ef5ae2394
								
							
						
					
					
						commit
						96545a899f
					
				| @ -159,8 +159,8 @@ the form: | ||||
| \frac{A_1 \ldots A_n} {B_1 \ldots B_m} | ||||
| {{< /latex >}} | ||||
| 
 | ||||
| This reads, "given that the premises \\(A\_1\\) through \\(A\_n\\) are true, | ||||
| it holds that the conclusions \\(B\_1\\) through \\(B\_m\\) are true". | ||||
| This reads, "given that the premises \(A_1\) through \(A_n\) are true, | ||||
| it holds that the conclusions \(B_1\) through \(B_m\) are true". | ||||
| 
 | ||||
| For example, we can have the following inference rule: | ||||
| 
 | ||||
| @ -173,18 +173,18 @@ For example, we can have the following inference rule: | ||||
| Since you wear a jacket when it's cold, and it's cold, we can conclude | ||||
| that you will wear a jacket. | ||||
| 
 | ||||
| When talking about type systems, it's common to represent a type with \\(\\tau\\). | ||||
| When talking about type systems, it's common to represent a type with \(\tau\). | ||||
| The letter, which is the greek character "tau", is used as a placeholder for | ||||
| some __concrete type__. It's kind of like a template, to be filled in | ||||
| with an actual value. When we plug in an actual value into a rule containing | ||||
| \\(\\tau\\), we say we are __instantiating__ it. Similarly, we will use | ||||
| \\(e\\) to serve as a placeholder for an expression (matched by our | ||||
| \\(A\_{add}\\) grammar rule from part 2). Next, we have the typing relation, | ||||
| written as \\(e:\\tau\\). This says that "expression \\(e\\) has the type | ||||
| \\(\\tau\\)". | ||||
| \(\tau\), we say we are __instantiating__ it. Similarly, we will use | ||||
| \(e\) to serve as a placeholder for an expression (matched by our | ||||
| \(A_{add}\) grammar rule from part 2). Next, we have the typing relation, | ||||
| written as \(e:\tau\). This says that "expression \(e\) has the type | ||||
| \(\tau\)". | ||||
| 
 | ||||
| Alright, this is enough to get us started with some typing rules. | ||||
| Let's start with one for numbers. If we define \\(n\\) to mean | ||||
| Let's start with one for numbers. If we define \(n\) to mean | ||||
| "any expression that is a just a number, like 3, 2, 6, etc.", | ||||
| we can write the typing rule as follows: | ||||
| 
 | ||||
| @ -206,30 +206,30 @@ Now, let's move on to the rule for function application: | ||||
| 
 | ||||
| This rule includes everything we've said before: | ||||
| the thing being applied has to have a function type | ||||
| (\\(\\tau\_1 \\rightarrow \\tau\_2\\)), and | ||||
| (\(\tau_1 \rightarrow \tau_2\)), and | ||||
| the expression the function is applied to | ||||
| has to have the same type \\(\\tau\_1\\) as the | ||||
| has to have the same type \(\tau_1\) as the | ||||
| left type of the function. | ||||
| 
 | ||||
| It's the variable rule that forces us to adjust our notation. | ||||
| Our rules don't take into account the context that we've | ||||
| already discussed, and thus, we can't bring | ||||
| in any outside information. Let's fix that! It's convention | ||||
| to use the symbol \\(\\Gamma\\) for the context. We then | ||||
| add notation to say, "using the context \\(\\Gamma\\), | ||||
| we can deduce that \\(e\\) has type \\(\\tau\\)". We will | ||||
| write this as \\(\\Gamma \\vdash e : \\tau\\). | ||||
| to use the symbol \(\Gamma\) for the context. We then | ||||
| add notation to say, "using the context \(\Gamma\), | ||||
| we can deduce that \(e\) has type \(\tau\)". We will | ||||
| write this as \(\Gamma \vdash e : \tau\). | ||||
| 
 | ||||
| But what __is__ our context? We can think of it | ||||
| as a mapping from variable names to their known types. We | ||||
| can represent such a mapping using a set of pairs | ||||
| in the form \\(x : \\tau\\), where \\(x\\) represents | ||||
| in the form \(x : \tau\), where \(x\) represents | ||||
| a variable name.  | ||||
| 
 | ||||
| Since \\(\\Gamma\\) is just a regular set, we can | ||||
| write \\(x : \\tau \\in \\Gamma\\), meaning that | ||||
| in the current context, it is known that \\(x\\) | ||||
| has the type \\(\\tau\\). | ||||
| Since \(\Gamma\) is just a regular set, we can | ||||
| write \(x : \tau \in \Gamma\), meaning that | ||||
| in the current context, it is known that \(x\) | ||||
| has the type \(\tau\). | ||||
| 
 | ||||
| Let's update our rules with this new addition. | ||||
| 
 | ||||
| @ -283,19 +283,19 @@ Let's first take a look at the whole case expression rule: | ||||
| This is a lot more complicated than the other rules we've seen, and we've used some notation | ||||
| that we haven't seen before. Let's take this step by step: | ||||
| 
 | ||||
| 1. \\(e : \\tau\\), in this case, means that the expression between `case` and `of`, is of type \\(\\tau\\). | ||||
| 2. \\(\\text{matcht}(\\tau, p\_i) = b\_i\\) means that the pattern \\(p\_i\\) can match a value of type | ||||
| \\(\\tau\\), producing additional type pairs \\(b\_i\\). We need \\(b\_i\\) because a pattern | ||||
| such as `Cons x xs` will introduce new type information, namely \\(\text{x} : \text{Int}\\) and \\(\text{xs} : \text{List}\\). | ||||
| 3. \\(\\Gamma,b\_i \\vdash e\_i : \\tau\_c\\) means that each individual branch can be deduced to have the type | ||||
| \\(\\tau\_c\\), using the previously existing context \\(\\Gamma\\), with the addition of \\(b\_i\\), the new type information. | ||||
| 4. Finally, the conclusion is that the case expression, if all the premises are met, is of type \\(\\tau\_c\\). | ||||
| 1. \(e : \tau\), in this case, means that the expression between `case` and `of`, is of type \(\tau\). | ||||
| 2. \(\text{matcht}(\tau, p_i) = b_i\) means that the pattern \(p_i\) can match a value of type | ||||
| \(\tau\), producing additional type pairs \(b_i\). We need \(b_i\) because a pattern | ||||
| such as `Cons x xs` will introduce new type information, namely \(\text{x} : \text{Int}\) and \(\text{xs} : \text{List}\). | ||||
| 3. \(\Gamma,b_i \vdash e_i : \tau_c\) means that each individual branch can be deduced to have the type | ||||
| \(\tau_c\), using the previously existing context \(\Gamma\), with the addition of \(b_i\), the new type information. | ||||
| 4. Finally, the conclusion is that the case expression, if all the premises are met, is of type \(\tau_c\). | ||||
| 
 | ||||
| For completeness, let's add rules for \\(\\text{matcht}(\\tau, p\_i) = b\_i\\). We'll need two: one for | ||||
| For completeness, let's add rules for \(\text{matcht}(\tau, p_i) = b_i\). We'll need two: one for | ||||
| the "basic" pattern, which always matches the value and binds a variable to it, and one | ||||
| for a constructor pattern, that matches a constructor and its parameters. | ||||
| 
 | ||||
| Let's define \\(v\\) to be a variable name in the context of a pattern. For the basic pattern: | ||||
| Let's define \(v\) to be a variable name in the context of a pattern. For the basic pattern: | ||||
| 
 | ||||
| {{< latex >}} | ||||
| \frac | ||||
| @ -303,7 +303,7 @@ Let's define \\(v\\) to be a variable name in the context of a pattern. For the | ||||
| {\text{matcht}(\tau, v) = \{v : \tau \}} | ||||
| {{< /latex >}} | ||||
| 
 | ||||
| For the next rule, let's define \\(c\\) to be a constructor name. The rule for the constructor pattern, then, is: | ||||
| For the next rule, let's define \(c\) to be a constructor name. The rule for the constructor pattern, then, is: | ||||
| 
 | ||||
| {{< latex >}} | ||||
| \frac | ||||
| @ -312,8 +312,8 @@ For the next rule, let's define \\(c\\) to be a constructor name. The rule for t | ||||
| {{< /latex >}} | ||||
| 
 | ||||
| This rule means that whenever we have a pattern in the form of a constructor applied to | ||||
| \\(n\\) variable names, if the constructor takes \\(n\\) arguments of types \\(\\tau\_1\\) | ||||
| through \\(\\tau\_n\\), then the each variable will have a corresponding type. | ||||
| \(n\) variable names, if the constructor takes \(n\) arguments of types \(\tau_1\) | ||||
| through \(\tau_n\), then the each variable will have a corresponding type. | ||||
| 
 | ||||
| We didn't include lambda expressions in our syntax, and thus we won't need typing rules for them, | ||||
| so it actually seems like we're done with the first draft of our type rules. | ||||
| @ -399,8 +399,8 @@ we're binding is the same as the string we're binding it to | ||||
| We now have a unification algorithm, but we still | ||||
| need to implement our rules. Our rules | ||||
| usually include three things: an environment | ||||
| \\(\\Gamma\\), an expression \\(e\\), | ||||
| and a type \\(\\tau\\). We will | ||||
| \(\Gamma\), an expression \(e\), | ||||
| and a type \(\tau\). We will | ||||
| represent this as a method on `ast`, our struct | ||||
| for an expression tree. This | ||||
| method will take an environment and return | ||||
| @ -413,7 +413,7 @@ to a type. So naively, we can implement this simply | ||||
| using an `std::map`. But observe | ||||
| that we only extend the environment in one case so far: | ||||
| a case expression. In a case expression, we have the base | ||||
| envrionment \\(\\Gamma\\), and for each branch, | ||||
| envrionment \(\Gamma\), and for each branch, | ||||
| we extend it with the bindings produced by | ||||
| the pattern match. Each branch receives a modified | ||||
| copy of the original environment, one that | ||||
| @ -459,7 +459,7 @@ We start with with a signature inside `ast`: | ||||
| ``` | ||||
| virtual type_ptr typecheck(type_mgr& mgr, const type_env& env) const; | ||||
| ``` | ||||
| We also implement the \\(\\text{matchp}\\) function | ||||
| We also implement the \(\text{matchp}\) function | ||||
| as a method `match` on `pattern` with the following signature: | ||||
| ``` | ||||
| virtual void match(type_ptr t, type_mgr& mgr, type_env& env) const; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user