Skip to content

Commit

Permalink
Update manual
Browse files Browse the repository at this point in the history
* Mention the mutually recursive nature of `letrec` bindings and fix
  iteration counts and some hashes
* Mention the fact that we can compose expressions with meta commands
  and briefly explain how they work
  • Loading branch information
arthurpaulino committed Oct 24, 2024
1 parent 8e523ec commit 9ec2374
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 19 deletions.
6 changes: 3 additions & 3 deletions src/03-lisp.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ But how do we name functions?
### Bindings

We'll come back to recursive functions in a bit.
First, let's see how `let` allows us to introduce varible bindings.
First, let's see how `let` allows us to introduce variable bindings.

```
lurk-user> (let ((a 1)) a)
Expand Down Expand Up @@ -275,7 +275,7 @@ lurk-user>
0
(+ n (sum-upto (- n 1)))))))
(sum-upto 5))
[54 iterations] => 15
[55 iterations] => 15
```

## Higher-order functions
Expand All @@ -292,7 +292,7 @@ lurk-user>
(map f (cdr list))))))
(square (lambda (x) (* x x))))
(map square '(1 2 3 4 5)))
[76 iterations] => (1 4 9 16 25)
[78 iterations] => (1 4 9 16 25)
```

By the way, how was the list `(1 2 3 4 5)` produced so easily?
Expand Down
26 changes: 18 additions & 8 deletions src/07-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ lurk-user> (current-env)
lurk-user> (let ((x 1)) (current-env))
[3 iterations] => <Env ((x . 1))>
lurk-user> (letrec ((x 1)) (current-env))
[3 iterations] => <Env ((x . <Thunk 1>))>
[4 iterations] => <Env ((x . <Thunk 1>))>
lurk-user> ((lambda (x) (current-env)) 1)
[4 iterations] => <Env ((x . 1))>
```
Expand Down Expand Up @@ -379,7 +379,9 @@ lurk-user> ((lambda () (emit 1) (emit 2)))

### `let`

`(let ((var binding)...) body)` extends the current environment with a set of variable bindings and then evaluate `body` in the updated environment. `let` is used for binding values to names and modifying environments. See also the `def` REPL meta command.
`(let ((var binding) ...) body)` extends the current environment with a set of variable bindings and then evaluate `body` in the updated environment.
`let` is used for binding values to names and modifying environments.
See also the `def` REPL meta command.

```
lurk-user> (let ((x 1) (y 2)) (+ x y))
Expand All @@ -399,15 +401,23 @@ lurk-user> (let ((a 1) (b 2)) (emit a) (emit b))

### `letrec`

`(letrec ((var binding)...) body)` is similar to `let`, but it enables recursion by allowing references to `var` inside its own `binding`. Generally, the binding is be a `lambda` expression representing a recursive function. See also the `defrec` REPL meta command.
`(letrec ((var binding) ...) body)` is similar to `let`, but it enables mutually recursive bindings.
See also the `defrec` REPL meta command.

```
lurk-user> (letrec ((x 1)) x)
[3 iterations] => 1
[4 iterations] => 1
lurk-user> (letrec ((x 1)) (current-env))
[3 iterations] => <Env ((x . <Thunk 1>))> ;; Thunks are the internal representation used for recursive evaluation
lurk-user> (letrec ((last (lambda (x) (if (cdr x) (last (cdr x)) (car x))))) (last '(1 2 3)))
[19 iterations] => 3
[4 iterations] => <Env ((x . <Thunk 1>))> ;; Thunks are the internal representation used for recursive evaluation
lurk-user>
(letrec ((last (lambda (x) (if (cdr x) (last (cdr x)) (car x)))))
(last '(1 2 3)))
[20 iterations] => 3
lurk-user>
(letrec ((odd? (lambda (n) (if (= n 0) nil (even? (- n 1)))))
(even? (lambda (n) (if (= n 0) t (odd? (- n 1))))))
(odd? 5))
[53 iterations] => t
```

Similarly to `let`, the body is interpreted as if there were a `begin` surrounding it.
Expand All @@ -416,7 +426,7 @@ Similarly to `let`, the body is interpreted as if there were a `begin` surroundi
lurk-user> (letrec ((a 1) (b 2)) (emit a) (emit b))
1
2
[7 iterations] => 2
[9 iterations] => 2
```

### `u64`
Expand Down
45 changes: 37 additions & 8 deletions src/08-repl.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Let's explore more of this tool we've been interacting with, the Lurk REPL.

## Meta commands

Until now, every Lurk expression we've evaluated was evaluated under an empty environment.
Trying to evaluate a dangling `a` would trigger an error indicating an unbound variable.

Expand Down Expand Up @@ -40,14 +42,41 @@ And it can also provide further help on specific meta commands.
lurk-user> !(help def)
def - Extends env with a non-recursive binding.
Info:
Gets macroexpanded to (let ((<symbol> <value>)) (current-env)).
Gets macroexpanded to (let ((<symbol> <expr>)) (current-env)).
The REPL's env is set to the result.
Usage: !(def <symbol> <value>)
Format: !(def <symbol> <expr>)
Example:
!(def foo (lambda () 123))
Returns: The binding symbol
```

As hinted above, meta commands have return values.
That's because we can build expressions that contain meta commands.
The meta commands will be evaluated first, having their occurrences in the original expression replaced by their respective return values.
The resulting expression is then evaluated.

```
lurk-user> (+ !(def b 21) b)
b
[2 iterations] => 42
```

When processing `(+ !(def b 21) b)`, `!(def b 21)` is evaluated, which adds `b` to the environment and returns the symbol `b`.
The resulting expression is `(+ b b)`, which, once evaluated, results in `42`.

To double check which expression was evaluated, you can enter the debug mode with `!(debug)` (make sure to check `!(help debug)`!).

This is what the debug mode shows:

```
?0: (+ b b)
?1: b
1: b ↦ 21
!1: b ↦ 21
0: (+ b b) ↦ 42
```

However, we need to go over a few abstractions in order to understand some meta commands you may encounter.
Now, let's go over a few abstractions in order to understand some meta commands you will encounter.

## Chaining callables

Expand Down Expand Up @@ -75,22 +104,22 @@ lurk-user>
(let ((counter (+ counter x)))
(cons counter (commit (add counter)))))))
(add 0)))
[6 iterations] => #c0x8b0d8bd2feef87f7347a8d2dbe7cc74ba045ec0f14c1417266e3f46d0a0ac5
[7 iterations] => #c0x64fee21bad514ff18399dfc5066caebf34acc0441c9af675ba95a998077591
```

And let's see what happens when we provide the argument `5` to it.

```
lurk-user> (#c0x8b0d8bd2feef87f7347a8d2dbe7cc74ba045ec0f14c1417266e3f46d0a0ac5 5)
[13 iterations] => (5 . #c0x60b9491bb451ddac036e2b9e77256da893f505c3b480b477599b1bfcb6f334)
lurk-user> (#c0x64fee21bad514ff18399dfc5066caebf34acc0441c9af675ba95a998077591 5)
[14 iterations] => (5 . #c0x73c6bd6ffb74c34b7c21e83aaaf71ddf919bb2a9c93ecb43c656f8dc67060a)
```

We get the current counter result and the next callable (a functional commitment in this example).
So let's provide the argument `3` to this next callable.

```
lurk-user> (#c0x60b9491bb451ddac036e2b9e77256da893f505c3b480b477599b1bfcb6f334 3)
[13 iterations] => (8 . #c0x4570f0489268d7f99f28b99aaa00ac16fa422e5c67d9eb20da573a071f2873)
lurk-user> (#c0x73c6bd6ffb74c34b7c21e83aaaf71ddf919bb2a9c93ecb43c656f8dc67060a 3)
[14 iterations] => (8 . #c0x67dfd6673beec5f6758e3404d74af882f66b1084adabb97bc27cba554def07)
```

The new result is `8` and we also get the next callable, as expected.
Expand Down

0 comments on commit 9ec2374

Please sign in to comment.