|
| 1 | +#lang scribble/manual |
| 2 | + |
| 3 | +@(require (for-label (except-in racket ...))) |
| 4 | +@(require redex/pict |
| 5 | + racket/runtime-path |
| 6 | + scribble/examples |
| 7 | + "hustle/semantics.rkt" |
| 8 | + "utils.rkt" |
| 9 | + "ev.rkt" |
| 10 | + "../utils.rkt") |
| 11 | + |
| 12 | +@(define codeblock-include (make-codeblock-include #'h)) |
| 13 | + |
| 14 | +@(for-each (λ (f) (ev `(require (file ,(path->string (build-path notes "iniquity" f)))))) |
| 15 | + '() #;'("interp.rkt" "compile.rkt" "asm/interp.rkt" "asm/printer.rkt")) |
| 16 | + |
| 17 | +@title[#:tag "Iniquity"]{Iniquity: function definitions and calls} |
| 18 | + |
| 19 | +@table-of-contents[] |
| 20 | + |
| 21 | +@section[#:tag-prefix "iniquity"]{Functions} |
| 22 | + |
| 23 | +Our programming languages so far have been impoverished in the |
| 24 | +following sense: in order to process arbitrarily large data, the |
| 25 | +programs themselves must be proportionally as large. Want to compute |
| 26 | +something over a billion element list? You'll need a billion |
| 27 | +expressions. Consequently, the expressiveness of our language is |
| 28 | +severely restricted. |
| 29 | + |
| 30 | +Let's now remove that restriction by incorporating @bold{functions}, |
| 31 | +and in particular, @bold{recursive functions}, which will allow us to |
| 32 | +compute over arbitrarily large data with finite-sized programs. |
| 33 | + |
| 34 | +Let's call it @bold{Iniquity}. |
| 35 | + |
| 36 | +We will extend the syntax by introducing a new syntactic category of |
| 37 | +programs, which have the shape: |
| 38 | + |
| 39 | +@racketblock[ |
| 40 | +(begin |
| 41 | + (define (_f0 _x0 ...) _e0) |
| 42 | + (define (_f1 _x1 ...) _e1) |
| 43 | + ... |
| 44 | + _e)] |
| 45 | + |
| 46 | +And the syntax of expressions will be extended to include function calls: |
| 47 | + |
| 48 | +@racketblock[ |
| 49 | +(_fi _e0 ...) |
| 50 | +] |
| 51 | + |
| 52 | +where @racket[_fi] is one of the function names defined in the program. |
| 53 | + |
| 54 | +Note that functions can have any number of parameters and, |
| 55 | +symmetrically, calls can have any number of arguments. A program |
| 56 | +consists of zero or more function definitions followed by an |
| 57 | +expression. |
| 58 | + |
| 59 | + |
| 60 | +@section[#:tag-prefix "iniquity"]{An Interpreter for Functions} |
| 61 | + |
| 62 | +Writing an interpreter for Inquity is not too hard. The main idea is |
| 63 | +that the interpretation of expression is now parameterized by a set of |
| 64 | +function definitions from the program. It serves as a second kind of |
| 65 | +environment that gets passed around and is used to resolve function |
| 66 | +definitions when interpreting function calls. |
| 67 | + |
| 68 | +The way a function call is interpreted is to first interpret all of |
| 69 | +the arguments, building up a list of results. Then the definition of |
| 70 | +the function being called is looked up. If the function has the same |
| 71 | +number of parameters as there are arguments in the call, the body of |
| 72 | +the function is interpreted in an enviorment that maps each parameter |
| 73 | +to to the corresponding argument. That's it. |
| 74 | + |
| 75 | +@codeblock-include["iniquity/interp.rkt"] |
| 76 | + |
| 77 | +A couple of things to note: |
| 78 | + |
| 79 | +@itemlist[ |
| 80 | + |
| 81 | +@item{since the function definition environment is passed along even |
| 82 | +when interpreting the body of function definitions, this |
| 83 | +interpretation supports recursion, and even mutual recursion.} |
| 84 | + |
| 85 | +@item{functions are @emph{not} values (yet). We cannot bind a |
| 86 | +variable to a function. We cannot make a list of functions. We |
| 87 | +cannot compute a function. The first position of a function call is a |
| 88 | +function @emph{name}, not an arbitrary expression. Nevertheless, we |
| 89 | +have significantly increased the expressivity of our language.} |
| 90 | + |
| 91 | +] |
| 92 | + |
0 commit comments