Start work on part 12 of compiler series
Some checks failed
continuous-integration/drone/push Build is failing
BIN
content/blog/12_compiler_let_in_lambda/fig_colored.png
Executable file
After Width: | Height: | Size: 396 KiB |
BIN
content/blog/12_compiler_let_in_lambda/fig_colored_ordered.png
Executable file
After Width: | Height: | Size: 457 KiB |
BIN
content/blog/12_compiler_let_in_lambda/fig_graph.png
Executable file
After Width: | Height: | Size: 117 KiB |
BIN
content/blog/12_compiler_let_in_lambda/fig_subgraphs.png
Executable file
After Width: | Height: | Size: 137 KiB |
BIN
content/blog/12_compiler_let_in_lambda/fig_subgraphs_colored.png
Executable file
After Width: | Height: | Size: 195 KiB |
BIN
content/blog/12_compiler_let_in_lambda/fig_subgraphs_colored_all.png
Executable file
After Width: | Height: | Size: 471 KiB |
BIN
content/blog/12_compiler_let_in_lambda/fig_subgraphs_colored_ordered.png
Executable file
After Width: | Height: | Size: 602 KiB |
85
content/blog/12_compiler_let_in_lambda/index.md
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
---
|
||||||
|
title: Compiling a Functional Language Using C++, Part 12 - Let/In and Lambdas
|
||||||
|
date: 2020-04-20T20:15:16-07:00
|
||||||
|
tags: ["C and C++", "Functional Languages", "Compilers"]
|
||||||
|
draft: true
|
||||||
|
---
|
||||||
|
|
||||||
|
Now that our language's type system is more fleshed out and pleasant to use,
|
||||||
|
it's time to shift our focus to the ergonomics of the language itself. I've been
|
||||||
|
mentioning `let/in` expressions and __lambda expressions__ for a while now.
|
||||||
|
The former will let us create names for expressions that are limited to
|
||||||
|
a certain scope (without having to create global variable bindings), while
|
||||||
|
the latter will allow us to create functions without giving them any name at
|
||||||
|
all.
|
||||||
|
|
||||||
|
Let's take a look at `let/in` expressions first, to make sure we're all on
|
||||||
|
the same page about what it is we're trying to implement. Let's
|
||||||
|
start with some rather basic examples, and then move on to more
|
||||||
|
complex examples. The most basic use of a `let/in` expression is, in Haskell:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
let x = 5 in x + x
|
||||||
|
```
|
||||||
|
|
||||||
|
In the above example, we bind the variable `x` to the value `5`, and then
|
||||||
|
refer to `x` twice in the expression after the `in`. The whole snippet is one
|
||||||
|
expression, evaluating to what the `in` part evaluates to. Additionally,
|
||||||
|
the variable `x` does not escape the expression -
|
||||||
|
{{< sidenote "right" "used-note" "it cannot be used anywhere else." >}}
|
||||||
|
Unless, of course, you bind it elsewhere; naturally, using <code>x</code>
|
||||||
|
here does not forbid you from re-using the variable.
|
||||||
|
{{< /sidenote >}}
|
||||||
|
|
||||||
|
Now, consider a slightly more complicated example:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
let sum xs = foldl (+) 0 xs in sum [1,2,3]
|
||||||
|
```
|
||||||
|
|
||||||
|
Here, we're defining a _function_ `sum`,
|
||||||
|
{{< sidenote "right" "eta-note" "which takes a single argument:" >}}
|
||||||
|
Those who favor the
|
||||||
|
<a href="https://en.wikipedia.org/wiki/Tacit_programming#Functional_programming">point-free</a>
|
||||||
|
programming style may be slightly twitching right now, the words
|
||||||
|
<em>eta reduction</em> swirling in their mind. What do you know,
|
||||||
|
<code>fold</code>-based <code>sum</code> is even one of the examples
|
||||||
|
on the Wikipedia page! I assure you, I left the code as you see it
|
||||||
|
deliberately, to demonstrate a principle.
|
||||||
|
{{< /sidenote >}} the list to be summed. We will want this to be valid
|
||||||
|
in our language, as well. We will soon see how this particular feature
|
||||||
|
is related to lambda functions, and why I'm covering these two features
|
||||||
|
in the same post.
|
||||||
|
|
||||||
|
Let's step up the difficulty a bit more, with an example that,
|
||||||
|
{{< sidenote "left" "translate-note" "though it does not immediately translate to our language," >}}
|
||||||
|
The part that doesn't translate well is the whole deal with patterns in
|
||||||
|
function arguments, as well as the notion of having more than one equation
|
||||||
|
for a single function, as is the case with <code>safeTail</code>.
|
||||||
|
<br><br>
|
||||||
|
It's not that these things are <em>impossible</em> to translate; it's just
|
||||||
|
that translating them may be worthy of a post in and of itself, and would only
|
||||||
|
serve to bloat and complicate this part. What can be implemented with
|
||||||
|
pattern arguments can just as well be implemented using regular case expressions;
|
||||||
|
I dare say most "big" functional languages actually just convert from the
|
||||||
|
former to the latter as part of the compillation process.
|
||||||
|
{{< /sidenote >}} illustrates another important principle:
|
||||||
|
|
||||||
|
```Haskell
|
||||||
|
let
|
||||||
|
safeTail [] = Nothing
|
||||||
|
safeTail [x] = Just x
|
||||||
|
safeTail (_:xs) = safeTail xs
|
||||||
|
myTail = safeTail [1,2,3,4]
|
||||||
|
in
|
||||||
|
myTail
|
||||||
|
```
|
||||||
|
|
||||||
|
The principle here is that definitions in `let/in` can be __recursive and
|
||||||
|
polymorphic__. Remember the note in
|
||||||
|
[part 10]({{< relref "10_compiler_polymorphism.md" >}}) about
|
||||||
|
[let-polymorphism](https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system#Let-polymorphism)? This is it: we're allowing polymorphic variable bindings,
|
||||||
|
but only when they're bound in a `let/in` expression (or at the top level).
|
||||||
|
|
||||||
|
The principles demonstrated by the last two snippets mean that compiling `let/in`
|
||||||
|
expressions, at least with the power we want to give them
|