Finish draft of part 12 of compiler series.

This commit is contained in:
Danila Fedorin 2020-06-20 22:03:57 -07:00
parent c496be1031
commit 971f58da9b
5 changed files with 218 additions and 1 deletions

View File

@ -0,0 +1,17 @@
data List a = { Nil, Cons a (List a) }
defn fix f = { let { defn x = { f x } } in { x } }
defn fixpointOnes fo = { Cons 1 fo }
defn sumTwo l = {
case l of {
Nil -> { 0 }
Cons x xs -> {
x + case xs of {
Nil -> { 0 }
Cons y ys -> { y }
}
}
}
}
defn main = { sumTwo (fix fixpointOnes) }

View File

@ -0,0 +1,19 @@
data List a = { Nil, Cons a (List a) }
defn sum l = {
case l of {
Nil -> { 0 }
Cons x xs -> { x + sum xs}
}
}
defn map f l = {
case l of {
Nil -> { Nil }
Cons x xs -> { Cons (f x) (map f xs) }
}
}
defn main = {
sum (map \x -> { x * x } (map (\x -> { x + x }) (Cons 1 (Cons 2 (Cons 3 Nil)))))
}

View File

@ -0,0 +1,47 @@
data Bool = { True, False }
data List a = { Nil, Cons a (List a) }
defn if c t e = {
case c of {
True -> { t }
False -> { e }
}
}
defn mergeUntil l r p = {
let {
defn mergeLeft nl nr = {
case nl of {
Nil -> { Nil }
Cons x xs -> { if (p x) (Cons x (mergeRight xs nr)) Nil }
}
}
defn mergeRight nl nr = {
case nr of {
Nil -> { Nil }
Cons x xs -> { if (p x) (Cons x (mergeLeft nl xs)) Nil }
}
}
} in {
mergeLeft l r
}
}
defn const x y = { x }
defn sum l = {
case l of {
Nil -> { 0 }
Cons x xs -> { x + sum xs }
}
}
defn main = {
let {
defn firstList = { Cons 1 (Cons 3 (Cons 5 Nil)) }
defn secondList = { Cons 2 (Cons 4 (Cons 6 Nil)) }
} in {
sum (mergeUntil firstList secondList (const True))
}
}

View File

@ -0,0 +1,23 @@
data Pair a b = { Pair a b }
defn packer = {
let {
data Packed a = { Packed a }
defn pack a = { Packed a }
defn unpack p = {
case p of {
Packed a -> { a }
}
}
} in {
Pair pack unpack
}
}
defn main = {
case packer of {
Pair pack unpack -> {
unpack (pack 3)
}
}
}

View File

@ -827,4 +827,115 @@ It's important to test all the language features that we just added. This
includes recursive definitions, nested function dependency cycles, and includes recursive definitions, nested function dependency cycles, and
uses of lambda functions. Some of the following examples will be rather uses of lambda functions. Some of the following examples will be rather
silly, but they should do a good job of checking that everything works silly, but they should do a good job of checking that everything works
as we expect. as we expect. Let's start with a simple use of a recursive definition
inside a `let/in`. A classic definition in that form is of `fix`
(the fixpoint combinator):
```Haskell
fix f = let x = f x in x
```
This defines `x` to be `f x`, which by substitution becomes `f (f x)`, and then
`f (f (f x))` and so on. The fixpoint combinator allows one to write a
recursive function that doesn't use its own name in the body. Rather,
we write a function expecting to receive 'itself' as a value:
```Haskell
fix :: (a -> a) -> a
factRec :: (Int -> Int) -> Int -> Int
factRec f x = if x == 0 then 1 else x * f x
fact :: Int -> Int
fact = fix factRec
```
Notice that `factRec` doesn't reference itself, but rather takes
as argument a function it expects to be 'factorial' called `f`,
and uses that in its recursive case. We can write something similar
in our language, perhaps to create an infinite list of ones:
{{< codeblock "text" "compiler/12/examples/fixpoint.txt" >}}
We want `sumTwo` to take the first two elements from the list,
and return their sum. For an infinite list of ones, we expect
this sum to equal to 2, and so it does:
```
Result: 2
```
Next, let's try to define a function which has a mutually recursive pair
of definitions inside of a `let/in`. Let's also make these expressions
reference a function from the global scope, so that we know our
dependency tracking works as expected:
{{< codeblock "text" "compiler/12/examples/letin.txt" >}}
Here, we have a function `mergeUntil` which, given two lists
and a predicate, combines the two lists until as long as
the predicate returns `True`. It does so using a convoluted
pair of two mutually recursive functions, one of which
unpacks the left list, and the other the right. Each of the
functions calls the global function `if`. We also use two
definitions inside of `main` to create the two lists we're
going to merge. The compiler outputs the following (correct)
types:
```
const: forall bb bc . bc -> bb -> bc
if: Bool* -> List* Int* -> List* Int* -> List* Int*
main: Int*
mergeUntil: List* Int* -> List* Int* -> (Int* -> Bool*) -> List* Int*
sum: List* Int* -> Int*
```
And the result is 21, as would be expected from the sum of the numbers 1-6:
```
Result: 21
```
Let's try lambda functions now. We can try use them for a higher-order function
like `map`:
{{< codeblock "text" "compiler/12/examples/lambda.txt" >}}
In this example, we first double every element in the list, then square it,
and finally take the sum. This should give us 4+16+36 = 56, and so it does:
```
Result: 56
```
Finally, let's do some magic with a locally-declared data type. We'll make a
"packer" that creates a wrapped instance of a type, `Packed a`. Since the
constructor of this data type is not globally visible, it's not possible
to get the value back out, except by using an 'unpacking' function that
we provide:
{{< codeblock "text" "compiler/12/examples/packed.txt" >}}
Here, the `packer` definition returns a pair of the 'packing'
and 'unpacking' functions. The 'packing' function simply applies
the consntructor of `Packed` to its argument, while the 'unpacking'
function performs pattern matching (which is possible since the
data type is still in scope there). We expect `unpack (pack 3)` to
return 3, and it does:
```
Result: 3
```
Trying to pattern match, though, doesn't work, just like we would want!
This is enough to convince me that our changes do, indeed, work! Of
the 'major' components that I wanted to cover, only __Input/Output__
remains! Additionally, a [lobste.rs](https://lobste.rs) user suggested
that we also cover namespacing, and perhaps we will look into that as well.
Before either of those things, though, I think that I want to go through
the compiler and perform another round of improvements, similarly to
[part 4]({{< relref "04_compiler_improvements" >}}). It's hard to do a lot
of refactoring while covering new content, since major changes need to
be explained and presented for the post to make sense. I hope to see
you in these future posts!