2015-01-19 05:04:01 +03:00
|
|
|
<div class="pagetitle">
|
2015-01-06 18:09:41 +03:00
|
|
|
![](img/titles/introduction.png)
|
2015-01-19 05:04:01 +03:00
|
|
|
</div>
|
2015-01-06 18:09:41 +03:00
|
|
|
|
2015-01-19 05:04:01 +03:00
|
|
|
<p class="halfbreak">
|
|
|
|
</p>
|
2015-01-06 18:09:41 +03:00
|
|
|
|
2015-01-19 05:04:01 +03:00
|
|
|
<!--
|
|
|
|
> *When the limestone of imperative programming is worn away, the granite of
|
|
|
|
> functional programming will be observed.*
|
2015-01-11 23:18:10 +03:00
|
|
|
>
|
2015-01-13 20:20:08 +03:00
|
|
|
> <cite>— Simon Peyton Jones</cite>
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
<p class="halfbreak">
|
|
|
|
</p>
|
2015-01-19 05:04:01 +03:00
|
|
|
-->
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
Introduction
|
|
|
|
============
|
|
|
|
|
|
|
|
Goals
|
|
|
|
-----
|
|
|
|
|
|
|
|
Off we go on our Adventure in Haskell Compilers! It will be intense, long,
|
|
|
|
informative, and hopefully fun.
|
|
|
|
|
|
|
|
It's important to stress several points about the goals before we start our
|
|
|
|
discussion:
|
|
|
|
|
|
|
|
a) This is not a rigorous introduction to type systems, it is a series of
|
|
|
|
informal discussions of topics structured around a reference implementation with
|
|
|
|
links provided to more complete and rigorous resources on the topic at hand. The
|
|
|
|
goal is to give you an overview of the concepts and terminology as well as a
|
|
|
|
simple reference implementation to play around with.
|
|
|
|
|
|
|
|
b) None of the reference implementations are industrial strength, many of them
|
|
|
|
gloss over fundamental issues that are left out for simplicity reasons. Writing
|
|
|
|
an industrial strength programming language involves work on the order of
|
|
|
|
hundreds of person-years and is an enormous engineering effort.
|
|
|
|
|
|
|
|
c) You should not use the reference compiler for anything serious. It is
|
|
|
|
intended for study and reference only.
|
|
|
|
|
|
|
|
Throughout our discussion we will stress the importance of semantics and the
|
|
|
|
construction of core calculi. The frontend language syntax will be in the
|
|
|
|
ML-family syntax out of convenience rather than principle. Choice of lexical
|
|
|
|
syntax is arbitrary, uninteresting, and quite often distracts from actual
|
2015-01-27 15:57:22 +03:00
|
|
|
substance in comparative language discussion. If there is one central theme it
|
|
|
|
is that the *design of the core calculus should drive development*, not the
|
2015-01-06 18:09:41 +03:00
|
|
|
frontend language.
|
|
|
|
|
|
|
|
Prerequisites
|
|
|
|
-------------
|
|
|
|
|
|
|
|
An intermediate understanding at the level of the *Real World Haskell* book is
|
|
|
|
recommended. We will shy away from advanced type-level programming that is
|
|
|
|
often present in modern Haskell and will make heavy use of more value-level
|
2015-01-06 18:49:27 +03:00
|
|
|
constructs. Although a strong familiarity with monads, monad transformers,
|
2015-01-06 18:09:41 +03:00
|
|
|
applicatives, and the standard Haskell data structures is strongly recommended.
|
|
|
|
|
|
|
|
Some familiarity with the standard 3rd party libraries will be useful. Many of
|
|
|
|
these are briefly overviewed in [What I Wish I Knew When Learning
|
|
|
|
Haskell](http://dev.stephendiehl.com/hask/).
|
|
|
|
|
|
|
|
In particular we will use:
|
|
|
|
|
|
|
|
* ``containers``
|
|
|
|
* ``unordered-containers``
|
|
|
|
* ``text``
|
|
|
|
* ``mtl``
|
|
|
|
* ``filepath``
|
|
|
|
* ``directory``
|
|
|
|
* ``process``
|
|
|
|
* ``parsec``
|
|
|
|
* ``pretty``
|
|
|
|
* ``wl-pprint``
|
|
|
|
* ``graphscc``
|
|
|
|
* ``haskeline``
|
|
|
|
* ``repline``
|
|
|
|
* ``cereal``
|
|
|
|
* ``deepseq``
|
|
|
|
* ``uniqueid``
|
|
|
|
* ``pretty-show``
|
|
|
|
* ``uniplate``
|
|
|
|
* ``optparse-applicative``
|
|
|
|
* ``unbound-generics``
|
|
|
|
* ``language-c-quote``
|
|
|
|
* ``bytestring``
|
|
|
|
* ``hoopl``
|
|
|
|
* ``fgl``
|
|
|
|
* ``llvm-general``
|
|
|
|
* ``smtLib``
|
|
|
|
* ``sbv``
|
|
|
|
|
|
|
|
In later chapters some experience with C, LLVM and x86 Assembly will be very
|
|
|
|
useful, although not strictly required.
|
|
|
|
|
|
|
|
Concepts
|
|
|
|
========
|
|
|
|
|
|
|
|
We are going to set out to build a *statically typed* *functional* programming
|
|
|
|
language with a *native code generation* *backend*. What does all this mean?
|
|
|
|
|
|
|
|
Functional Languages
|
|
|
|
--------------------
|
|
|
|
|
|
|
|
In mathematics a *function* is defined as a correspondence that assigns exactly
|
|
|
|
one element of a set to each element in another set. If a function $f(x) = a$
|
|
|
|
then the function evaluated at $x$ will always have the value $a$. Central to
|
2015-01-27 15:57:22 +03:00
|
|
|
the notion of all mathematics is the notion of *equational reasoning*, where
|
2015-01-06 18:09:41 +03:00
|
|
|
if $a= f(x)$ then for an expression $g(f(x), f(x))$, this is always equivalent
|
2015-05-17 09:10:04 +03:00
|
|
|
to $g(a, a)$. In other words, the values computed by functions can always be
|
2015-01-06 18:09:41 +03:00
|
|
|
substituted freely at all occurrences.
|
|
|
|
|
|
|
|
The central idea of *functional programming* is to structure our programs in
|
2015-01-27 15:57:22 +03:00
|
|
|
such a way that we can reason about them as a system of equations just like
|
|
|
|
we can in mathematics. The evaluation of a pure function is one in which *side
|
2015-05-17 09:10:04 +03:00
|
|
|
effects* are prohibited; a function may only return a result without altering
|
2015-01-06 18:09:41 +03:00
|
|
|
the world in any *observable* way.
|
|
|
|
|
|
|
|
The implementation may perform effects, but central to this definition is the
|
2015-01-10 22:19:54 +03:00
|
|
|
unobservability of such effects. A function is said to be *referentially
|
2015-01-06 17:28:11 +03:00
|
|
|
transparent* if replacing a function with its computed value output yields the
|
2015-01-06 18:09:41 +03:00
|
|
|
same observable behavior.
|
|
|
|
|
|
|
|
By contrast impure functions are ones which allow unrestricted and observable
|
|
|
|
side effects. The invocation of an impure function always allows for the
|
|
|
|
possibility of performing any functionality before yielding a value.
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
// impure: mutation side effects
|
|
|
|
function f() {
|
|
|
|
x += 3;
|
|
|
|
return 42;
|
|
|
|
}
|
|
|
|
|
|
|
|
// impure: international side effects
|
|
|
|
function f() {
|
|
|
|
launchMissiles();
|
|
|
|
return 42;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The behavior of a pure function is independent of where and when it is
|
2015-02-01 17:05:54 +03:00
|
|
|
evaluated, whereas the behavior of an impure function is intrinsically tied to
|
|
|
|
its execution order.
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
Functional programming is defined simply as programming strictly with pure
|
|
|
|
referentially transparent functions.
|
|
|
|
|
|
|
|
Static Typing
|
|
|
|
-------------
|
|
|
|
|
|
|
|
*Types* are a formal language integrated with a programming language that
|
|
|
|
refines the space of allowable behavior and degree of expressible programs for
|
|
|
|
the language. Types are the world's most popular formal method for analyzing
|
|
|
|
programs.
|
|
|
|
|
2015-01-19 05:04:01 +03:00
|
|
|
In a language like Python all expressions have the same type at compile time,
|
|
|
|
and all syntactically valid programs can be evaluated. In the case where the
|
2015-02-01 17:07:02 +03:00
|
|
|
program is nonsensical the runtime will bubble up exceptions during evaluation.
|
|
|
|
The Python interpreter makes no attempt to analyze the given program for
|
|
|
|
soundness at all before running it.
|
2015-01-19 05:04:01 +03:00
|
|
|
|
|
|
|
```bash
|
|
|
|
>>> True & "false"
|
|
|
|
Traceback (most recent call last):
|
|
|
|
File "<stdin>", line 1, in <module>
|
|
|
|
TypeError: unsupported operand type(s) for &: 'bool' and 'str'
|
|
|
|
```
|
2015-01-06 18:09:41 +03:00
|
|
|
|
2015-01-19 05:04:01 +03:00
|
|
|
By comparison Haskell will do quite a bit of work to try to ensure that the
|
|
|
|
program is well-defined before running it. The language that we use to
|
|
|
|
predescribe and analyze static semantics of the program is that of *static
|
|
|
|
types*.
|
2015-01-06 18:09:41 +03:00
|
|
|
|
2015-01-19 05:04:01 +03:00
|
|
|
```bash
|
|
|
|
Prelude> True && "false"
|
|
|
|
|
|
|
|
<interactive>:2:9:
|
|
|
|
Couldn't match expected type `Bool' with actual type `[Char]'
|
|
|
|
In the second argument of `(&&)', namely `"false"'
|
|
|
|
In the expression: True && "false"
|
|
|
|
In an equation for `it': it = True && "false"
|
|
|
|
```
|
2015-01-06 18:09:41 +03:00
|
|
|
|
2015-01-19 05:04:01 +03:00
|
|
|
Catching minor type mismatch errors is the simplest example of usage, although
|
|
|
|
they occur extremely frequently as we humans are quite fallible in our reasoning
|
2015-01-27 18:16:55 +03:00
|
|
|
about even the simplest of program constructions! Although this is just the tip
|
|
|
|
of the iceberg, the gradual trend over the last 20 years goes toward more
|
|
|
|
*expressive types* in modern type systems which are capable of guaranteeing a
|
|
|
|
large variety of program correctness properties.
|
2015-01-19 05:04:01 +03:00
|
|
|
|
|
|
|
* Preventing resource allocation errors.
|
2015-02-01 17:07:51 +03:00
|
|
|
* Enforcing security in communication protocols.
|
2015-01-19 05:04:01 +03:00
|
|
|
* Side effect management.
|
|
|
|
* Preventing buffer overruns.
|
|
|
|
* Ensuring cryptographic properties for network protocols.
|
2015-01-27 15:57:22 +03:00
|
|
|
* Modeling and verifying theorems in mathematics and logic.
|
2015-01-19 05:04:01 +03:00
|
|
|
* Preventing data races and deadlocks in concurrent systems.
|
|
|
|
|
2015-01-30 20:11:41 +03:00
|
|
|
Even though type systems will never be able to capture all aspects of a
|
|
|
|
program, more sophisticated type systems are increasingly able to model a large
|
|
|
|
space of program behavior.
|
|
|
|
They are one of the most exciting areas of modern computer science research.
|
|
|
|
Put most bluntly, **static types let you be dumb** and offload the
|
2015-01-19 05:04:01 +03:00
|
|
|
checking that you would otherwise have to do in your head to a system that can
|
|
|
|
do the reasoning for you and work with you to interactively build your program.
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
Functional Compilers
|
|
|
|
--------------------
|
|
|
|
|
2015-01-19 05:04:01 +03:00
|
|
|
A *compiler* is a program for turning high-level representation of ideas in a
|
|
|
|
human readable language into another form. A compiler is typically divided into
|
|
|
|
parts, a *frontend* and a *backend*. These are loose terms but the frontend
|
|
|
|
typically deals with converting the human representation of the code into some
|
|
|
|
canonicalized form while the backend converts the canonicalized form into
|
|
|
|
another form that is suitable for evaluation.
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
The high level structure of our functional compiler is described by the
|
|
|
|
following *block diagram*. Each describes a *phase* which is a sequence of
|
|
|
|
transformations composed to transform the input program.
|
|
|
|
|
|
|
|
![](img/pipeline1.png)
|
|
|
|
|
2015-01-12 20:38:46 +03:00
|
|
|
* **Source** - The frontend textual source language.
|
|
|
|
* **Parsing** - Source is parsed into an abstract syntax tree.
|
|
|
|
* **Desugar** - Redundant structure from the frontend language is removed and canonicalized.
|
|
|
|
* **Type Checking** - The program is type-checked and/or type-inferred yielding an explicitly typed form.
|
|
|
|
* **Transformation** - The core language is transformed to prepare for compilation.
|
|
|
|
* **Compilation** - The core language is lowered into a form to be compiled or interpreted.
|
|
|
|
* **(Code Generation)** - Platform specific code is generated, linked into a binary.
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
A *pass* may transform the input program from one form into another or alter the
|
|
|
|
internal state of the compiler context. The high level description of the forms
|
|
|
|
our final compiler will go through is the following sequence:
|
|
|
|
|
|
|
|
![](img/pipeline2.png)
|
|
|
|
|
|
|
|
Internal forms used during compilation are *intermediate representations* and
|
2015-01-10 22:19:54 +03:00
|
|
|
typically any non-trivial language will involve several.
|
2015-01-06 18:09:41 +03:00
|
|
|
|
2015-01-19 05:04:01 +03:00
|
|
|
Parsing
|
|
|
|
-------
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
The source code is simply the raw sequence of text that specifies the program.
|
|
|
|
Lexing splits the text stream into a sequence of *tokens*. Only the presence of
|
2015-05-17 09:10:04 +03:00
|
|
|
invalid symbols is checked; programs that are meaningless in other ways are
|
2015-01-27 18:16:55 +03:00
|
|
|
accepted.
|
2015-01-12 20:38:46 +03:00
|
|
|
Whitespace is either ignored or represented as a unique token in the stream.
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
```haskell
|
|
|
|
let f x = x + 1
|
|
|
|
```
|
|
|
|
|
2015-01-12 20:38:46 +03:00
|
|
|
For instance the previous program might generate a token stream like the
|
|
|
|
following:
|
2015-01-06 18:09:41 +03:00
|
|
|
|
2015-01-19 05:04:01 +03:00
|
|
|
```haskell
|
|
|
|
[
|
|
|
|
TokenLet,
|
|
|
|
TokenSym "f",
|
|
|
|
TokenSym "x",
|
|
|
|
TokenEq,
|
|
|
|
TokenSym "x",
|
|
|
|
TokenAdd,
|
|
|
|
TokenNum 1
|
|
|
|
]
|
|
|
|
```
|
2015-01-06 18:09:41 +03:00
|
|
|
|
2015-05-17 09:11:05 +03:00
|
|
|
We can then scan the token stream via dispatch on predefined patterns of
|
|
|
|
tokens called *productions*, and recursively build up the syntax datatype for
|
|
|
|
the *abstract syntax tree* (AST).
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
```haskell
|
|
|
|
type Name = String
|
|
|
|
|
|
|
|
data Expr
|
|
|
|
= Var Name
|
2015-01-06 18:55:26 +03:00
|
|
|
| Lit Lit
|
2015-01-06 18:09:41 +03:00
|
|
|
| Op PrimOp [Expr]
|
|
|
|
| Let Name [Name] Expr
|
|
|
|
|
2015-01-08 05:33:59 +03:00
|
|
|
data Lit
|
2015-01-06 18:09:41 +03:00
|
|
|
= LitInt Int
|
2015-01-06 18:55:26 +03:00
|
|
|
|
|
|
|
data PrimOp
|
|
|
|
= Add
|
2015-01-06 18:09:41 +03:00
|
|
|
```
|
|
|
|
|
|
|
|
So for example the following string is parsed into the resulting ``Expr`` value.
|
|
|
|
|
|
|
|
```haskell
|
|
|
|
let f x = x + 1
|
|
|
|
```
|
|
|
|
|
|
|
|
```haskell
|
|
|
|
Let "f" ["x"] (Op Add [Var "x", Lit (LitInt 1)])
|
|
|
|
```
|
|
|
|
|
|
|
|
Desugaring
|
|
|
|
----------
|
|
|
|
|
|
|
|
Desugaring is the process by which the frontend AST is transformed into a
|
|
|
|
simpler form of itself by reducing the number of complex structures by
|
|
|
|
expressing them in terms of a fixed set of simpler constructs.
|
|
|
|
|
|
|
|
Haskell's frontend is very large and many constructs are simplified down. For
|
2015-01-27 15:57:22 +03:00
|
|
|
example ``where`` clauses and operator sections are the most common examples.
|
2015-01-06 18:09:41 +03:00
|
|
|
Where clauses are effectively syntactic sugar for let bindings and operator
|
|
|
|
sections are desugared into lambdas with the left or right
|
2015-01-10 22:19:54 +03:00
|
|
|
hand side argument assigned to a fresh variable.
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
Type Inference
|
|
|
|
--------------
|
|
|
|
|
|
|
|
Type inference is the process by which the untyped syntax is endowed with type
|
|
|
|
information by a process known as *type reconstruction* or *type inference*. The
|
2015-01-27 18:16:55 +03:00
|
|
|
inference process may take into account explicit type annotations.
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
```haskell
|
|
|
|
let f x = x + 1
|
|
|
|
```
|
|
|
|
|
|
|
|
```haskell
|
|
|
|
Let "f" [] (Lam "x" (Op Add [Var "x", Lit (LitInt 1)]))
|
|
|
|
```
|
|
|
|
|
|
|
|
Inference will generate a system of constraints which are solved via a process
|
|
|
|
known as *unification* to yield the type of the expression.
|
|
|
|
|
|
|
|
```haskell
|
2015-01-08 05:33:59 +03:00
|
|
|
Int -> Int -> Int ~ a -> b
|
2015-01-06 18:09:41 +03:00
|
|
|
b ~ Int -> c
|
|
|
|
```
|
|
|
|
|
|
|
|
```haskell
|
|
|
|
f :: Int -> Int
|
|
|
|
```
|
|
|
|
|
|
|
|
In some cases this type will be incorporated directly into the AST and the
|
|
|
|
inference will transform the frontend language into an explicitly typed *core
|
2015-01-10 22:19:54 +03:00
|
|
|
language*.
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
```haskell
|
2015-01-08 05:33:59 +03:00
|
|
|
Let "f" []
|
|
|
|
(Lam "x"
|
2015-01-06 18:09:41 +03:00
|
|
|
(TArr TInt TInt)
|
2015-01-08 05:33:59 +03:00
|
|
|
(App
|
|
|
|
(App
|
2015-01-06 18:09:41 +03:00
|
|
|
(Prim "primAdd") (Var "x"))
|
|
|
|
(Lit (LitInt 1))))
|
|
|
|
```
|
|
|
|
|
2015-01-19 05:04:01 +03:00
|
|
|
Transformation
|
|
|
|
--------------
|
|
|
|
|
|
|
|
The type core representation is often suitable for evaluation, but quite often
|
|
|
|
different intermediate representations are more amenable to certain
|
2015-02-01 17:09:24 +03:00
|
|
|
optimizations and make various semantic properties of the language explicit.
|
2015-01-19 05:04:01 +03:00
|
|
|
These kind of intermediate forms will often attach information about free
|
2015-02-01 17:09:56 +03:00
|
|
|
variables, allocations, and usage information directly in the AST structure.
|
2015-01-19 05:04:01 +03:00
|
|
|
|
|
|
|
The most important form we will use is called the *Spineless Tagless G-Machine*
|
|
|
|
( STG ), an abstract machine that makes many of the properties of lazy
|
|
|
|
evaluation explicit directly in the AST.
|
|
|
|
|
2015-01-06 18:09:41 +03:00
|
|
|
Code Generation
|
|
|
|
---------------
|
|
|
|
|
2015-08-08 12:09:42 +03:00
|
|
|
After translating an expression to the core language we will either evaluate
|
|
|
|
it with a high-level interpreter written in Haskell itself, or translate it
|
|
|
|
to another intermediate language (such as LLVM IR or GHC's Cmm) which can be
|
|
|
|
compiled into native code. This intermediate language abstracts over the
|
|
|
|
process of assigning values to, and moving values between CPU registers and
|
|
|
|
main memory.
|
|
|
|
|
|
|
|
As an example, the statement
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
```haskell
|
|
|
|
let f x = x + 1
|
|
|
|
```
|
|
|
|
|
2015-08-08 12:09:42 +03:00
|
|
|
which corresponds to the following assembly instructions:
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
```haskell
|
|
|
|
f:
|
|
|
|
mov res, arg
|
|
|
|
add res, 1
|
|
|
|
ret
|
|
|
|
```
|
|
|
|
|
2015-08-08 12:09:42 +03:00
|
|
|
looks like this in LLVM IR (note the absence of mov instructions):
|
|
|
|
|
2015-01-06 18:09:41 +03:00
|
|
|
```haskell
|
|
|
|
define i32 @f(i32 %x) {
|
|
|
|
entry:
|
|
|
|
%add = add nsw i32 %x, 1
|
|
|
|
ret i32 %add
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2015-08-08 12:09:42 +03:00
|
|
|
From the intermediate representation the code can be compiled into the
|
|
|
|
system's assembly language. Any additional code that is required for
|
|
|
|
evaluation is *linked* into the resulting module.
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
```perl
|
|
|
|
f:
|
2015-01-19 05:04:01 +03:00
|
|
|
movl %edi, -4(%rsp)
|
|
|
|
movl -4(%rsp), %edi
|
|
|
|
addl $1, %edi
|
|
|
|
movl %edi, %eax
|
|
|
|
ret
|
2015-01-06 18:09:41 +03:00
|
|
|
|
|
|
|
```
|
|
|
|
|
2015-01-06 22:17:28 +03:00
|
|
|
And ultimately this code will be assembled into platform specific instructions by
|
2015-01-06 18:09:41 +03:00
|
|
|
the *native code generator*, encoded as a predefined sequence of CPU
|
|
|
|
instructions defined by the processor specification.
|
|
|
|
|
|
|
|
```perl
|
|
|
|
0000000000000000 <f>:
|
2015-01-19 05:04:01 +03:00
|
|
|
0: 89 7c 24 fc mov %edi,-0x4(%rsp)
|
|
|
|
4: 8b 7c 24 fc mov -0x4(%rsp),%edi
|
|
|
|
8: 81 c7 01 00 00 00 add $0x1,%edi
|
|
|
|
e: 89 f8 mov %edi,%eax
|
|
|
|
10: c3 retq
|
2015-01-06 18:09:41 +03:00
|
|
|
```
|
2015-01-09 07:46:30 +03:00
|
|
|
|
|
|
|
\pagebreak
|