From 31c583947d854a0d7de5280347de0b240904f1ab Mon Sep 17 00:00:00 2001 From: =?utf8?q?Rapha=C3=ABl=20Van=20Dyck?= Date: Sun, 28 Dec 2025 20:00:38 +0100 Subject: [PATCH] use MathJax to render mathematics; revise user manual and tutorial --- package-lock.json | 109 +++++++++++ package.json | 3 + src/ide.jsx | 5 +- system-files/TUTORIAL | 369 +++++++++++++++++++------------------- system-files/USER-MANUAL | 285 ++++++++++++++--------------- system-files/all-caps.css | 4 + system-files/all-caps.js | 80 +++++++++ system-files/evl2html.js | 4 + webpack.config.js | 22 ++- 9 files changed, 554 insertions(+), 327 deletions(-) diff --git a/package-lock.json b/package-lock.json index feeddd0..691d264 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "@codemirror/lang-html": "^6.4.11", "@codemirror/lang-javascript": "^6.2.4", "@codemirror/lang-xml": "^6.1.0", + "@mathjax/mathjax-stix2-font": "^4.1.0", "@radix-ui/colors": "^3.0.0", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-icons": "^1.3.2", @@ -22,6 +23,7 @@ "dotenv": "^17.2.3", "express": "^5.2.1", "jszip": "^3.10.1", + "mathjax": "^4.1.0", "react": "^19.2.3", "react-dom": "^19.2.3", "ua-parser-js": "^2.0.7" @@ -37,6 +39,7 @@ "@lezer/generator": "^1.8.0", "adm-zip": "^0.5.16", "babel-loader": "^10.0.0", + "copy-webpack-plugin": "^13.0.1", "css-loader": "^7.1.2", "eslint": "^9.39.2", "eslint-plugin-react-hooks": "^7.0.1", @@ -2348,6 +2351,18 @@ "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", "license": "MIT" }, + "node_modules/@mathjax/mathjax-newcm-font": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@mathjax/mathjax-newcm-font/-/mathjax-newcm-font-4.1.0.tgz", + "integrity": "sha512-n10AwYubUa2hyOzxSRzkwRrgCVns083zkentryXICMPKaWT/watfvK2sUk5D9Bow9mpDfoqb5EWApuUvqnlzaw==", + "license": "Apache-2.0" + }, + "node_modules/@mathjax/mathjax-stix2-font": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@mathjax/mathjax-stix2-font/-/mathjax-stix2-font-4.1.0.tgz", + "integrity": "sha512-uNRwwdyAJhdPwh7tFWrN0td+hT2lix8qDRELxNQWiH9ki1Z8Jb6NfTUzbMHyTiqZdNh2P+h1+QjPSnVlI6fsUw==", + "license": "Apache-2.0" + }, "node_modules/@nicolo-ribaudo/chokidar-2": { "version": "2.1.8-no-fsevents.3", "resolved": "https://registry.npmjs.org/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz", @@ -4366,6 +4381,43 @@ "node": ">=6.6.0" } }, + "node_modules/copy-webpack-plugin": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.1.tgz", + "integrity": "sha512-J+YV3WfhY6W/Xf9h+J1znYuqTye2xkBUIGyTPWuBAT27qajBa5mR4f8WBmfDY3YjRftT2kqZZiLi1qf0H+UOFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob-parent": "^6.0.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2", + "tinyglobby": "^0.2.12" + }, + "engines": { + "node": ">= 18.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/core-js-compat": { "version": "3.47.0", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.47.0.tgz", @@ -6357,6 +6409,15 @@ "node": ">= 0.4" } }, + "node_modules/mathjax": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mathjax/-/mathjax-4.1.0.tgz", + "integrity": "sha512-53eDXzxk40pS2sdI6KDCPoreY95ADaGygbi41ExKmn3FYQ+QIdpquIU90eppecelzQjf74kpScyeplVPccnIJw==", + "license": "Apache-2.0", + "dependencies": { + "@mathjax/mathjax-newcm-font": "^4.1.0" + } + }, "node_modules/media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", @@ -8169,6 +8230,54 @@ "dev": true, "license": "MIT" }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", diff --git a/package.json b/package.json index eb87634..ba3710e 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@codemirror/lang-html": "^6.4.11", "@codemirror/lang-javascript": "^6.2.4", "@codemirror/lang-xml": "^6.1.0", + "@mathjax/mathjax-stix2-font": "^4.1.0", "@radix-ui/colors": "^3.0.0", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-icons": "^1.3.2", @@ -22,6 +23,7 @@ "dotenv": "^17.2.3", "express": "^5.2.1", "jszip": "^3.10.1", + "mathjax": "^4.1.0", "react": "^19.2.3", "react-dom": "^19.2.3", "ua-parser-js": "^2.0.7" @@ -37,6 +39,7 @@ "@lezer/generator": "^1.8.0", "adm-zip": "^0.5.16", "babel-loader": "^10.0.0", + "copy-webpack-plugin": "^13.0.1", "css-loader": "^7.1.2", "eslint": "^9.39.2", "eslint-plugin-react-hooks": "^7.0.1", diff --git a/src/ide.jsx b/src/ide.jsx index 917b11d..b963eda 100644 --- a/src/ide.jsx +++ b/src/ide.jsx @@ -199,7 +199,10 @@ class AllCapsBuffer extends FileBuffer { const jsURL = URL.createObjectURL(jsBlob); const state = this.transaction.state; const text = state.sliceDoc(); - const html = text.replaceAll('___cssURL___', cssURL).replaceAll('___jsURL___', jsURL).replaceAll('___windowId___', window.id); + let html = text; + html = html.replaceAll('___cssURL___', cssURL); + html = html.replaceAll('___jsURL___', jsURL); + html = html.replaceAll('___windowId___', window.id); setTimeout(() => toggleHTMLModeCommand2(ide, window, html, cssURL, jsURL)); } } diff --git a/system-files/TUTORIAL b/system-files/TUTORIAL index 958b28c..7613694 100644 --- a/system-files/TUTORIAL +++ b/system-files/TUTORIAL @@ -15,33 +15,33 @@

This section introduces the global functions and macros that will be used in the tutorial. Some global functions and macros are included for the sake of completeness and will not actually be used in the tutorial.

In template function calls, argument names imply the following type restrictions:

In template macro calls, names enclosed in angle brackets have the following meanings:

It is customary for a function or macro that tests a condition and returns a boolean to have a name ending with a question mark. This rule has some notable exceptions, though.

Data Type boolean

-
(not boolean)
+
(not $\boolean$)
The function returns #t if its argument is #f and #f if its argument is #t.
> (not #t)
#f

> (not #f)
#t
-
(and <test-forms>)
+
(and $\metavar{test-forms}$)
The macro returns #t if all its arguments are #t and #f otherwise. The exact behavior of the macro is as follows: The macro evaluates the test-forms in sequence from left to right. If a test-form evaluates to an object that is not a boolean, then the following test-forms are not evaluated and the evaluation of the macro call completes abnormally. If a test-form evaluates to #f, then the following test-forms are not evaluated and the macro call evaluates to #f. If all test-forms evaluate to #t, then the macro call evaluates to #t. Note that the previous condition is automatically satisfied if there are no test-forms.
> (and)
#t

> (and #t)
#t

> (and #f)
#f

> (and #t #t)
#t

> (and #t #f)
#f

> (and #f #t)
#f

> (and #f #f)
#f
-
(or <test-forms>)
+
(or $\metavar{test-forms}$)
The macro returns #f if all its arguments are #f and #t otherwise. The exact behavior of the macro is as follows: The macro evaluates the test-forms in sequence from left to right. If a test-form evaluates to an object that is not a boolean, then the following test-forms are not evaluated and the evaluation of the macro call completes abnormally. If a test-form evaluates to #t, then the following test-forms are not evaluated and the macro call evaluates to #t. If all test-forms evaluate to #f, then the macro call evaluates to #f. Note that the previous condition is automatically satisfied if there are no test-forms.
> (or)
#f

> (or #t)
#t

> (or #f)
#f

> (or #t #t)
#t

> (or #t #f)
#t

> (or #f #t)
#t

> (or #f #f)
#f
@@ -49,208 +49,209 @@

The macros and and or stop evaluating their operands as soon as they can determine that the result of the macro call is definitively true or definitively false. This short-circuiting behavior is possible only because and and or are macros. When a function call is evaluated, all the operands are always evaluated (unless the evaluation of the operator or the evaluation of one of the operands other than the last one completes abnormally or does not complete).

Here are two expansions illustrating the implementations of the macros and and or:

The name of the function not and the names of the macros and and or do not end with a question mark because the function and the macros do not really test a condition. Instead, they combine booleans (they are boolean operators).

Data Type number

Arithmetic Operators

-
(+ number1numbern)
+
(+ $\number_1\ldots\number_n$)
If the function is invoked on zero numbers, then the number 0 is returned. If the function is invoked on one number, then that number is returned. If the function is invoked on more than one number, then the result of adding those numbers from left to right is returned: the second number is added to the first number, then the third number is added to the partial result just computed, … The function is a closure built on top of the primitive function _+, which must be invoked on exactly two numbers.
> (+)
0

> (+ 1)
1

> (+ 1 2)
3

> (+ 1 2 3)
6
-
(- number1numbern)
+
(- $\number_1\ldots\number_n$)
If the function is invoked on zero numbers, then the invocation completes abnormally. If the function is invoked on one number, then the opposite of that number is returned. If the function is invoked on more than one number, then the result of subtracting those numbers from left to right is returned: the second number is subtracted from the first number, then the third number is subtracted from the partial result just computed, … The function is a closure built on top of the primitive function _-, which must be invoked on exactly two numbers.
> (-)
ERROR: Expecting at least one number.

> (- 1)
-1

> (- 0 1)
-1

> (- 0 1 2)
-3

> (- 0 1 2 3)
-6
-
(* number1numbern)
+
(* $\number_1\ldots\number_n$)
If the function is invoked on zero numbers, then the number 1 is returned. If the function is invoked on one number, then that number is returned. If the function is invoked on more than one number, then the result of multiplying those numbers from left to right is returned: the first number is multiplied by the second number, then the partial result just computed is multiplied by the third number, … The function is a closure built on top of the primitive function _*, which must be invoked on exactly two numbers.
> (*)
1

> (* 2)
2

> (* 2 4)
8

> (* 2 4 8)
64
-
(/ number1numbern)
+
(/ $\number_1\ldots\number_n$)
If the function is invoked on zero numbers, then the invocation completes abnormally. If the function is invoked on one number, then the inverse of that number is returned. If the function is invoked on more than one number, then the result of dividing those numbers from left to right is returned: the first number is divided by the second number, then the partial result just computed is divided by the third number, … The function is a closure built on top of the primitive function _/, which must be invoked on exactly two numbers.
> (/)
ERROR: Expecting at least one number.

> (/ 2)
0.5

> (/ 1 2)
0.5

> (/ 1 2 4)
0.125

> (/ 1 2 4 8)
0.015625

Comparison Operators

-
(= number1 number2)
+
(= $\number_1$ $\number_2$)
The function returns #t if its two arguments are equal and #f otherwise.
-
(/= number1 number2)
+
(/= $\number_1$ $\number_2$)
The function returns #t if its two arguments are different and #f otherwise.
-
(< number1 number2)
+
(< $\number_1$ $\number_2$)
The function returns #t if its first argument is less than its second argument and #f otherwise.
-
(<= number1 number2)
+
(<= $\number_1$ $\number_2$)
The function returns #t if its first argument is less than or equal to its second argument and #f otherwise.
-
(> number1 number2)
+
(> $\number_1$ $\number_2$)
The function returns #t if its first argument is greater than its second argument and #f otherwise.
-
(>= number1 number2)
+
(>= $\number_1$ $\number_2$)
The function returns #t if its first argument is greater than or equal to its second argument and #f otherwise.
> (list (= -1 0) (= 0 0) (= 1 0))
(#f #t #f)

> (list (/= -1 0) (/= 0 0) (/= 1 0))
(#t #f #t)

> (list (< -1 0) (< 0 0) (< 1 0))
(#t #f #f)

> (list (<= -1 0) (<= 0 0) (<= 1 0))
(#t #t #f)

> (list (> -1 0) (> 0 0) (> 1 0))
(#f #f #t)

> (list (>= -1 0) (>= 0 0) (>= 1 0))
(#f #t #t)

The names of the comparison operators do not end with a question mark because they traditionally do not (in mathematics and other programming languages).

Data Type list

-
(list? object)
+
(list? $\object$)
The function returns #t if its argument is of type list and #f otherwise.
> (list? '())
#t

> (list? '(1 2 3))
#t
-
(list object1objectn)
-
The function collects its arguments into a list: when invoked on the arguments object1, …, objectn, the function returns a list whose elements are object1, …, objectn.
+
(list $\object_1\ldots\object_n$)
+
The function collects its arguments into a list: when invoked on the arguments $\object_1$, …, $\object_n$, the function returns a list whose elements are $\object_1$, …, $\object_n$.
> (list)
()

> (list 1 2 3)
(1 2 3)

Data Type empty-list

-
(empty-list? object)
+
(empty-list? $\object$)
The function returns #t if its argument is of type empty-list and #f otherwise.
> (empty-list? '())
#t

> (empty-list? '(1 2 3))
#f

Data Type cons

-
(cons? object)
+
(cons? $\object$)
The function returns #t if its argument is of type cons and #f otherwise.
> (cons? '())
#f

> (cons? '(1 2 3))
#t
-
(cons object1 object2)
-
The function returns a new cons whose first element is object1 and whose second element is object2.
+
(cons $\object_1$ $\object_2$)
+
The function returns a new cons whose first element is $\object_1$ and whose second element is $\object_2$.
> (cons 3 '())
(3)

> (cons 2 (cons 3 '()))
(2 3)

> (cons 1 (cons 2 (cons 3 '())))
(1 2 3)
-
(car cons)
+
(car $\cons$)
The function returns the first element of its argument.
-
(cdr cons)
+
(cdr $\cons$)
The function returns the second element of its argument.
> (car '(1 2 3))
1

> (cdr '(1 2 3))
(2 3)

> (car (cdr '(1 2 3)))
2

> (cdr (cdr '(1 2 3)))
(3)

> (car (cdr (cdr '(1 2 3))))
3

> (cdr (cdr (cdr '(1 2 3))))
()

Equality Predicates

The purpose of an equality predicate is to test the sameness of two objects. An equality predicate returns #t if the two objects are the same and #f otherwise. Because there are multiple notions of sameness, there are multiple equality predicates. The three main equality predicates are eq?, eql?, and equal?.

-
(eq? object1 object2)
+
(eq? $\object_1$ $\object_2$)
The function returns #t if and only if the two objects are one and the same. In other words, the function returns #t if and only if the two objects have the same address in the heap.
-
(eql? object1 object2)
+
(eql? $\object_1$ $\object_2$)
If both objects are of type number, then the function returns #t if and only if the two objects represent the same mathematical number. Otherwise, if both objects are of type character, then the function returns #t if and only if the two objects represent the same Unicode character. Otherwise, if both objects are of type string, then the function returns #t if and only if the two objects represent the same indexed sequence of Unicode characters. Otherwise, the function returns #t if and only if the two objects are eq?.
-
(equal? object1 object2)
+
(equal? $\object_1$ $\object_2$)
If both objects are of type cons, then the function returns #t if and only if the cars of the two objects are equal? and the cdrs of the two objects are equal?. Otherwise, if both objects are of type vector, then the function returns #t if and only if the two objects have the same length and their corresponding elements are equal?. Otherwise, the function returns #t if and only if the two objects are eql?.

The three main equality predicates are related as follows:

Of the three main equality predicates, eq? is the most discriminating and equal? is the least discriminating.

Because the three main equality predicates all return #f when testing the sameness of two objects of different leaf types, which one to use depends only on their behaviors when testing the sameness of two objects of the same leaf type.

-

Because there exists in the heap exactly one object of type void, two objects of type void are necessarily one and the same. Therefore, the appropriate equality predicates to test the sameness of two objects of type void are eq? and the equivalent eql? and equal?. (The fact that there exists in the heap exactly one object of type void means that the language cannot distinguish between two different missing objects, or two different undefined objects, or one missing object and one undefined object.)

+

By design, any two objects of type void are considered to be the same object. Because there exists in the heap exactly one object of type void, two objects of type void are necessarily one and the same. Therefore, the appropriate equality predicates to test the sameness of two objects of type void are eq? and the equivalent eql? and equal?.

When testing the sameness of two objects of type boolean, we want to test if the two objects represent the same truth value. Because there exists in the heap exactly one object of type boolean representing true and exactly one object of type boolean representing false, representing the same truth value is equivalent to being one and the same. Therefore, the appropriate equality predicates to test the sameness of two objects of type boolean are eq? and the equivalent eql? and equal?.

When testing the sameness of two objects of type number, we want to test if the two objects represent the same mathematical number. Because there can exist in the heap more than one object of type number representing the same mathematical number, representing the same mathematical number is not equivalent to being one and the same. Therefore, eq? is not an appropriate equality predicate to test the sameness of two objects of type number. The appropriate equality predicates are eql? and the equivalent equal?.

When testing the sameness of two objects of type character, we want to test if the two objects represent the same Unicode character. Because there can exist in the heap more than one object of type character representing the same Unicode character, representing the same Unicode character is not equivalent to being one and the same. Therefore, eq? is not an appropriate equality predicate to test the sameness of two objects of type character. The appropriate equality predicates are eql? and the equivalent equal?.

When testing the sameness of two objects of type string, we want to test if the two objects represent the same indexed sequence of Unicode characters. Because there can exist in the heap more than one object of type string representing the same indexed sequence of Unicode characters, representing the same indexed sequence of Unicode characters is not equivalent to being one and the same. Therefore, eq? is not an appropriate equality predicate to test the sameness of two objects of type string. The appropriate equality predicates are eql? and the equivalent equal?.

-

Using eq? to test the sameness of two objects of type number, character, or string is never safe because the evaluator is free to make copies of objects of those types at any time. Let's illustrate the point by considering the form ((_vlambda (x) (eq? x x)) 0). It is not guaranteed that the object of type number created by the reader, the value of the variable x, the first argument passed to the function eq?, and the second argument passed to the function eq? are one and the same. If the two objects passed to the function eq? are one and the same, then the test evaluates to #t. If the two objects passed to the function eq? are not one and the same, then the test evaluates to #f. Even in this seemingly straightforward case, the result of the test is unpredictable. The same goes for objects of type character or string.

-

The identity of an object of type keyword or variable is determined by its name. When testing the sameness of two objects of type keyword or variable, we want to test if the two objects have the same name. Because there cannot exist in the heap more than one object of type keyword or variable with the same name, having the same name is equivalent to being one and the same. Therefore, the appropriate equality predicates to test the sameness of two objects of type keyword or variable are eq? and the equivalent eql? and equal?.

-

Because there exists in the heap exactly one object of type empty-list, two objects of type empty-list are necessarily one and the same. Therefore, the appropriate equality predicates to test the sameness of two objects of type empty-list are eq? and the equivalent eql? and equal?. (The fact that there exists in the heap exactly one object of type empty-list means that the language cannot distinguish between two different empty lists of objects.)

-

When testing the sameness of two objects of type cons or two objects of type vector, we can use eq? or the equivalent eql? to test if the two objects are one and the same or we can use equal? to test if the two objects have the same elements.

+

Using eq? to test the sameness of two objects of type number, two objects of type character, or two objects of type string is never safe because the evaluator is free to make copies of objects of those types at any time. Let us illustrate the point by considering the form ((_vlambda (x) (eq? x x)) 0). It is not guaranteed that the object of type number created by the reader, the value of the variable x, the first argument passed to the function eq?, and the second argument passed to the function eq? are one and the same. If the two objects passed to the function eq? happen to be one and the same, then the test evaluates to #t. If the two objects passed to the function eq? happen not to be one and the same, then the test evaluates to #f. Even in this seemingly straightforward case, the result of the test is unpredictable. The same goes for objects of type character and string.

+

By design, any two objects of type keyword/variable sharing the same name are considered to be the same object. Because there cannot exist in the heap more than one object of type keyword/variable with the same name, having the same name is equivalent to being one and the same. Therefore, the appropriate equality predicates to test the sameness of two objects of type keyword/variable are eq? and the equivalent eql? and equal?.

+

By design, any two objects of type empty-list are considered to be the same object. Because there exists in the heap exactly one object of type empty-list, two objects of type empty-list are necessarily one and the same. Therefore, the appropriate equality predicates to test the sameness of two objects of type empty-list are eq? and the equivalent eql? and equal?.

+

When testing the sameness of two objects of type cons or two objects of type vector, we can use eq? or the equivalent eql? to test if the two objects are one and the same or we can use equal? to test if the two objects have the same elements. Whether to use (1) eq? or the equivalent eql? or (2) equal? is usually obvious from the purpose of the test.

When testing the sameness of two objects of type primitive-function or two objects of type closure, we would like to test if the two objects have the same behavior (same input/output mapping and same side effects). In practice, we can only test if the two objects are one and the same by using eq? or the equivalent eql? and equal?. For objects of type primitive-function, being one and the same is equivalent to having the same behavior. For objects of type closure, being one and the same implies having the same behavior but the converse is not true.

-

When testing the sameness of two objects that can each be of type type1, type2, …, we must use an equality predicate that is appropriate to test the sameness of two objects of type type1, two objects of type type2, ….

-

As a matter of style, when more than one equality predicate is appropriate, we should use the most discriminating one. As a matter of style again, when testing the sameness of two objects that can only be of type number, we should use the comparison operator = instead of the equality predicate eql?.

+

When testing the sameness of two objects that can each be of type $type_1$, $type_2$, …, an equality predicate that is appropriate to test the sameness of two objects of type $type_1$, two objects of type $type_2$, … must be used.

+

As a matter of style, when more than one equality predicate is appropriate, the most discriminating one should be used. As a matter of style again, when testing the sameness of two objects that can only be of type number, the comparison operator = should be used instead of the equality predicate eql?.

Global Definitions

-
(vdef <variable> <value-form>)
+
(vdef $\metavar{variable}$ $\metavar{value-form}$)
The purpose of the macro is to define a global variable by ensuring that the variable is bound in the value namespace of the global environment to the primary value of the value-form. The macro call evaluates to the variable.
-
(fdef <variable> <parameter-list> <body>)
-
The purpose of the macro is to define a global function by ensuring that the variable is bound in the function namespace of the global environment to the closure resulting from the evaluation of the _vlambda-form (_vlambda <parameter-list> <body>). The macro call evaluates to the variable.
-
(mdef <variable> <parameter-list> <body>)
-
The purpose of the macro is to define a global macro by ensuring that the variable is bound in the function namespace of the global environment to the closure resulting from the evaluation of the _mlambda-form (_mlambda <parameter-list> <body>). The macro call evaluates to the variable.
+
(fdef $\metavar{variable}$ $\metavar{parameter-list}$ $\metavar{body}$)
+
The purpose of the macro is to define a global function by ensuring that the variable is bound in the function namespace of the global environment to the closure resulting from the evaluation of the _vlambda-form (_vlambda $\metavar{parameter-list}$ $\metavar{body}$). The macro call evaluates to the variable.
+
(mdef $\metavar{variable}$ $\metavar{parameter-list}$ $\metavar{body}$)
+
The purpose of the macro is to define a global macro by ensuring that the variable is bound in the function namespace of the global environment to the closure resulting from the evaluation of the _mlambda-form (_mlambda $\metavar{parameter-list}$ $\metavar{body}$). The macro call evaluates to the variable.

Evaluation and Invocation Traces

An evaluation trace is a structured recording of some or all of the evaluations, invocations, and various steps performed by the evaluator to evaluate a form. It is assumed that the evaluation of the form completes normally, which implies that all evaluations and invocations entailed by the evaluation of the form also complete normally. Evaluation traces have the following format:

An evaluation trace is a tool used to illustrate a point. Any evaluation, invocation, or step that is not necessary to illustrate the point can be omitted from the evaluation trace. The only constraint is that if an evaluation or invocation is included in the evaluation trace, then both the in line and the matching out line must be included.

-

Let X be an evaluation or invocation and Y be another evaluation or invocation. A consequence of the evaluation rules is that X and Y cannot overlap. If we place X-in, X-out, Y-in, and Y-out on a line where time flows from left to right, there are four possible configurations and two impossible configurations:

+

Let $X$ be an evaluation or invocation and $Y$ be another evaluation or invocation. A consequence of the evaluation rules is that $X$ and $Y$ cannot overlap. If we place $X$-in, $X$-out, $Y$-in, and $Y$-out on a line where time flows from left to right, there are four possible configurations and two impossible configurations:

-

To make nesting more obvious, the lines located between a pair of matching in and out lines are indented to the right with respect to the matching lines. To make the connection between an in line and the matching out line more obvious, non-adjacent matching lines are connected by a vertical line.

+

To make nesting more obvious, the lines located between a pair of matching in and out lines are indented to the right with respect to the matching lines. To make the connection between an in line and the matching out line more obvious, nonadjacent matching lines are connected by a vertical line.

An invocation trace is an evaluation trace that only records invocations. Macro invocations are almost always omitted from an invocation trace.

-

Here is an evaluation trace of the evaluation of the form (+ 1 2). It will be assumed, wrongly, that the definition of the global function + is (fdef + (x y) (_+ x y)):

+

Here is an evaluation trace of the evaluation of the top-level form (+ 1 2). It will be assumed, wrongly, that the definition of the global function + is (fdef + (x y) (_+ x y)):

-

Let's consider the following global macro, whose purpose is to evaluate the form with respect to a lexical environment extending the current lexical environment to bind, in the value namespace, the variable to the primary value of the value-form:

+

Let us consider the following global macro, whose purpose is to evaluate the form with respect to a lexical environment extending the current lexical environment to bind, in the value namespace, the variable to the primary value of the value-form:

> (mdef simple-vlet (variable value-form form)
(list (list '_vlambda (list variable) form) value-form))
simple-vlet

> (simple-vlet x 1 (+ x 2))
3

> (simple-vlet x 1 (simple-vlet y 2 (+ x y)))
3
-

Here is an evaluation trace of the evaluation of the form (simple-vlet x 1 (+ x 2)):

+

Here is an evaluation trace of the evaluation of the top-level form (simple-vlet x 1 (+ x 2)):

-

Here is an evaluation trace of the evaluation of the form (simple-vlet x 1 (simple-vlet y 2 (+ x y))):

+

Here is an evaluation trace of the evaluation of the top-level form (simple-vlet x 1 (simple-vlet y 2 (+ x y))):

-

When debugging a macro, it is often useful to examine some expansions generated by the macro. The expansion of the macro call (<macro-name> <operand-1> … <operand-n>) can easily be obtained by evaluating the function call ((fref <macro-name>) '<operand-1> … '<operand-n>):

+

When debugging a macro, it is often useful to examine some expansions generated by the macro. The expansion of the macro call ($\metavar{macro-operator}$ $\metavarn{macro-operand}{1}\ldots\metavarn{macro-operand}{n}$) can easily be obtained by evaluating the plain function call ((fref $\metavar{macro-operator}$) $\code{'}\metavarn{macro-operand}{1}\ldots\code{'}\metavarn{macro-operand}{n}$):

> ((fref simple-vlet) 'x '1 '(+ x 2))
((_vlambda (x) (+ x 2)) 1)

> ((fref simple-vlet) 'x '1 '(simple-vlet y 2 (+ x y)))
((_vlambda (x) (simple-vlet y 2 (+ x y))) 1)

> ((fref simple-vlet) 'y '2 '(+ x y))
((_vlambda (y) (+ x y)) 2)

> ((fref simple-vlet) 'x '1 ((fref simple-vlet) 'y '2 '(+ x y)))
((_vlambda (x) ((_vlambda (y) (+ x y)) 2)) 1)
-

Let's consider the following global function, which returns the absolute value of its argument:

+

Let us consider the following global function, which returns the absolute value of its argument:

> (fdef abs (x)
(if (>= x 0) x (- x)))
abs

> (abs 1)
1

> (abs -1)
1
-

Here is an evaluation trace of the evaluation of the form (abs 1):

+

Here is an evaluation trace of the evaluation of the top-level form (abs 1):

-

Here is an evaluation trace of the evaluation of the form (abs -1):

+

Here is an evaluation trace of the evaluation of the top-level form (abs -1):

-

Let's consider the following global functions:

+

Let us consider the following global functions:

> (fdef sum-of-squares (x y)
(+ (square x) (square y)))
sum-of-squares

> (fdef square (x)
(* x x))
square

> (sum-of-squares 3 4)
25

Here is an invocation trace of the evaluation of the form (sum-of-squares 3 4):

@@ -489,28 +490,28 @@

Recursive Functions

-

A recursive function is a function that invokes itself directly (ff) or indirectly (fg→…→f).

+

A recursive function is a function that invokes itself directly ($f\rightarrow f$) or indirectly ($f\rightarrow g\rightarrow\cdots\rightarrow f$).

A recursive function call is a function call through which a recursive function invokes itself directly or indirectly.

-

A form is said to be in tail position with respect to a lambda-form if and only if one of the following mutually exclusive conditions is satisfied:

+

A form is said to be in tail position with respect to a lambda abstraction if and only if one of the following mutually exclusive conditions is satisfied:

-

A form in tail position with respect to a lambda-form has the following property: If (1) a closure resulting from the evaluation of the lambda-form is invoked and (2) the form happens to be evaluated during the invocation of the closure, then the result of the evaluation of the form becomes the result of the invocation of the closure without any further processing. (The values of the form are returned as is and not used otherwise.)

+

A form in tail position with respect to a lambda abstraction has the following property: If (1) a closure resulting from the evaluation of the lambda abstraction is invoked and (2) the form happens to be evaluated during the invocation of the closure, then the result of the evaluation of the form becomes the result of the invocation of the closure without any further processing. (The values of the form are returned as is and not used otherwise.)

Factorial Function

-

The factorial function is defined by the following recurrence relation, where n is a non-negative integer:

+

The factorial function is defined by the following recurrence relation, where $n$ is a nonnegative integer:

-

It follows from the definition that the factorial of n is equal to the product of the first n strictly positive integers:

-
n! = 1 × 2 × 3 × ⋯ × n
-

The equality still holds for n = 0 because the product is then empty and an empty product is, by convention, equal to 1.

-

Here are the values of the factorial function for n varying from 0 to 10:

- - - +

It follows from the definition that the factorial of $n$ is equal to the product of the first $n$ strictly positive integers:

+ $$n!=1\times2\times3\times\cdots\times n$$ +

The equality still holds for $n=0$ because the product is then empty and an empty product is, by convention, equal to $1$.

+

Here are the values of the factorial function for $n$ varying from $0$ to $10$:

+
0!1!2!3!4!5!6!7!8!9!10!
1126241207205040403203628803628800
+ +
$0!$$1!$$2!$$3!$$4!$$5!$$6!$$7!$$8!$$9!$$10!$
$1$$1$$2$$6$$24$$120$$720$$5040$$40320$$362880$$3628800$

The values of the factorial function can be computed by the global function fact, which is a direct translation of the recurrence relation:

> (fdef fact (n)
(if (= n 0) 1 (* n (fact (- n 1)))))
fact

> (fact 10)
3628800
@@ -558,17 +559,17 @@
  • The global function fact returns the number 720.
  • -

    There are two phases in the evaluation of the form (fact 6): (1) an expansion phase during which the number of active invocations of fact increases and (2) a contraction phase during which the number of active invocations of fact decreases. When the evaluator is processing the invocation of fact on the number 0, there are 7 active invocations of fact:

    +

    There are two phases in the evaluation of the form (fact 6): (1) an expansion phase during which the number of active invocations of fact increases and (2) a contraction phase during which the number of active invocations of fact decreases. When the evaluator is processing the invocation of fact on the number 0, there are $7$ active invocations of fact:

      -
    1. One evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 6. That invocation is waiting for the value of the recursive invocation of fact on the number (- n 1) = 5. When that value is eventually available, it will be multiplied by the number n = 6 and the result of the multiplication will be returned as the value of the invocation.
    2. -
    3. One evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 5. That invocation is waiting…
    4. -
    5. One evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 4. That invocation is waiting…
    6. -
    7. One evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 3. That invocation is waiting…
    8. -
    9. One evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 2. That invocation is waiting…
    10. -
    11. One evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 1. That invocation is waiting for the value of the recursive invocation of fact on the number (- n 1) = 0. When that value is eventually available, it will be multiplied by the number n = 1 and the result of the multiplication will be returned as the value of the invocation.
    12. -
    13. One evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 0. That invocation directly returns the number 1 as its value without any further recursive invocation of fact.
    14. +
    15. One invocation evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 6. That invocation is waiting for the value of the recursive invocation of fact on the number (- n 1) = 5. When that value is eventually available, it will be multiplied by the number n = 6 and the result of the multiplication will be returned as the value of the invocation.
    16. +
    17. One invocation evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 5. That invocation is waiting…
    18. +
    19. One invocation evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 4. That invocation is waiting…
    20. +
    21. One invocation evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 3. That invocation is waiting…
    22. +
    23. One invocation evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 2. That invocation is waiting…
    24. +
    25. One invocation evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 1. That invocation is waiting for the value of the recursive invocation of fact on the number (- n 1) = 0. When that value is eventually available, it will be multiplied by the number n = 1 and the result of the multiplication will be returned as the value of the invocation.
    26. +
    27. One invocation evaluating the body of fact with respect to a lexical environment binding, in the value namespace, the variable n to the number 0. That invocation directly returns the number 1 as its value without any further recursive invocation of fact.
    -

    The factorial is computed during the contraction phase by adding factors to a running product. The running product is initialized to the number 1 by the innermost invocation and the whole product is equal to 6 × (5 × (4 × (3 × (2 × (1 × 1))))).

    +

    The factorial is computed during the contraction phase by adding factors to a running product. The running product is initialized to the number 1 by the innermost invocation and the whole product is equal to $6\times(5\times(4\times(3\times(2\times(1\times1)))))$.

    The values of the factorial function can also be computed by the global function fact-iter:

    > (fdef fact-iter (n)
    (fact-iter-internal n 1))
    fact-iter

    > (fdef fact-iter-internal (n acc)
    (if (= n 0) acc (fact-iter-internal (- n 1) (* n acc))))
    fact-iter-internal

    > (fact-iter 10)
    3628800

    The bulk of the work is done by the auxiliary global function fact-iter-internal, which contains one recursive function call. The call to fact-iter-internal in fact-iter and the recursive function call in fact-iter-internal are both in tail position.

    @@ -621,29 +622,29 @@
  • The global function fact-iter returns the number 720.
  • -

    There are two phases in the evaluation of the form (fact-iter 6): (1) an expansion phase during which the number of active invocations increases and (2) a contraction phase during which the number of active invocations decreases. When the evaluator is processing the invocation of fact-iter-internal on the numbers 0 and 720, there are 8 active invocations (1 of fact-iter and 7 of fact-iter-internal):

    +

    There are two phases in the evaluation of the form (fact-iter 6): (1) an expansion phase during which the number of active invocations increases and (2) a contraction phase during which the number of active invocations decreases. When the evaluator is processing the invocation of fact-iter-internal on the numbers 0 and 720, there are $8$ active invocations ($1$ of fact-iter and $7$ of fact-iter-internal):

      -
    1. One evaluating the body of fact-iter with respect to a lexical environment binding, in the value namespace, the variable n to the number 6. That invocation is waiting for the value of the invocation of fact-iter-internal on the numbers n = 6 and 1. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    2. -
    3. One evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 6 and the variable acc to the number 1. That invocation is waiting for the value of the recursive invocation of fact-iter-internal on the numbers (- n 1) = 5 and (* n acc) = 6. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    4. -
    5. One evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 5 and the variable acc to the number 6. That invocation is waiting…
    6. -
    7. One evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 4 and the variable acc to the number 30. That invocation is waiting…
    8. -
    9. One evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 3 and the variable acc to the number 120. That invocation is waiting…
    10. -
    11. One evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 2 and the variable acc to the number 360. That invocation is waiting…
    12. -
    13. One evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 1 and the variable acc to the number 720. That invocation is waiting for the value of the recursive invocation of fact-iter-internal on the numbers (- n 1) = 0 and (* n acc) = 720. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    14. -
    15. One evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 0 and the variable acc to the number 720. That invocation directly returns the number acc = 720 as its value without any further invocation of fact-iter-internal.
    16. +
    17. One invocation evaluating the body of fact-iter with respect to a lexical environment binding, in the value namespace, the variable n to the number 6. That invocation is waiting for the value of the invocation of fact-iter-internal on the numbers n = 6 and 1. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    18. +
    19. One invocation evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 6 and the variable acc to the number 1. That invocation is waiting for the value of the recursive invocation of fact-iter-internal on the numbers (- n 1) = 5 and (* n acc) = 6. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    20. +
    21. One invocation evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 5 and the variable acc to the number 6. That invocation is waiting…
    22. +
    23. One invocation evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 4 and the variable acc to the number 30. That invocation is waiting…
    24. +
    25. One invocation evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 3 and the variable acc to the number 120. That invocation is waiting…
    26. +
    27. One invocation evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 2 and the variable acc to the number 360. That invocation is waiting…
    28. +
    29. One invocation evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 1 and the variable acc to the number 720. That invocation is waiting for the value of the recursive invocation of fact-iter-internal on the numbers (- n 1) = 0 and (* n acc) = 720. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    30. +
    31. One invocation evaluating the body of fact-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 0 and the variable acc to the number 720. That invocation directly returns the number acc = 720 as its value without any further recursive invocation of fact-iter-internal.
    -

    The factorial is computed during the expansion phase by adding factors to a running product. The running product, which is initialized to the number 1 by fact-iter, is propagated up the invocation chain through the variable acc. The whole product, which is equal to 1 × (2 × (3 × (4 × (5 × (6 × 1))))), is propagated down the invocation chain without any further processing.

    +

    The factorial is computed during the expansion phase by adding factors to a running product. The running product, which is initialized to the number 1 by fact-iter, is propagated up the invocation chain through the variable acc. The whole product, which is equal to $1\times(2\times(3\times(4\times(5\times(6\times1)))))$, is propagated down the invocation chain without any further processing.

    Fibonacci Sequence

    -

    The Fibonacci sequence is defined by the following recurrence relation, where n is a non-negative integer:

    +

    The Fibonacci sequence is defined by the following recurrence relation, where $n$ is a nonnegative integer:

    -

    Here are the values of the Fibonacci sequence for n varying from 0 to 10:

    - - - +

    Here are the values of the Fibonacci sequence for $n$ varying from $0$ to $10$:

    +
    F0F1F2F3F4F5F6F7F8F9F10
    011235813213455
    + +
    $F_0$$F_1$$F_2$$F_3$$F_4$$F_5$$F_6$$F_7$$F_8$$F_9$$F_{10}$
    $0$$1$$1$$2$$3$$5$$8$$13$$21$$34$$55$

    The values of the Fibonacci sequence can be computed by the global function fib, which is a direct translation of the recurrence relation:

    > (fdef fib (n)
    (if (= n 0)
    0
    (if (= n 1)
    1
    (+ (fib (- n 1)) (fib (- n 2))))))
    fib

    > (fib 10)
    55
    @@ -799,26 +800,26 @@
  • The global function fib returns the number 8.
  • -

    During the evaluation of the form (fib 6), the global function fib is invoked 1 time on the number 6, 1 time on the number 5, 2 times on the number 4, 3 times on the number 3, 5 times on the number 2, 8 times on the number 1, and 5 times on the number 0.

    -

    When the evaluator is processing the first invocation of fib on the number 0 (that invocation has a gray background in the invocation trace), there are 6 active invocations of fib:

    +

    During the evaluation of the form (fib 6), the global function fib is invoked $1$ time on the number 6, $1$ time on the number 5, $2$ times on the number 4, $3$ times on the number 3, $5$ times on the number 2, $8$ times on the number 1, and $5$ times on the number 0.

    +

    When the evaluator is processing the first invocation of fib on the number 0 (that invocation has a gray background in the invocation trace), there are $6$ active invocations of fib:

      -
    1. One evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 6. That invocation is waiting for the value of the recursive invocation of fib on the number (- n 1) = 5. When that value is eventually available, it will be stored in a temporary location and fib will be invoked recursively on the number (- n 2) = 4. When the value of the second recursive invocation of fib is eventually available, it will be added to the stored value of the first recursive invocation of fib and the result of the addition will be returned as the value of the invocation.
    2. -
    3. One evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 5. That invocation is waiting…
    4. -
    5. One evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 4. That invocation is waiting…
    6. -
    7. One evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 3. That invocation is waiting for the value of the recursive invocation of fib on the number (- n 1) = 2. When that value is eventually available, it will be stored in a temporary location and fib will be invoked recursively on the number (- n 2) = 1. When the value of the second recursive invocation of fib is eventually available, it will be added to the stored value of the first recursive invocation of fib and the result of the addition will be returned as the value of the invocation.
    8. -
    9. One evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 2. That invocation has already stored in a temporary location the value of the recursive invocation of fib on the number (- n 1) = 1 and is waiting for the value of the recursive invocation of fib on the number (- n 2) = 0. When the value of the second recursive invocation of fib is eventually available, it will be added to the stored value of the first recursive invocation of fib and the result of the addition will be returned as the value of the invocation.
    10. -
    11. One evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 0. That invocation directly returns the number 0 as its value without any further invocation of fib.
    12. +
    13. One invocation evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 6. That invocation is waiting for the value of the recursive invocation of fib on the number (- n 1) = 5. When that value is eventually available, it will be stored in a temporary location and fib will be invoked recursively on the number (- n 2) = 4. When the value of the second recursive invocation of fib is eventually available, it will be added to the stored value of the first recursive invocation of fib and the result of the addition will be returned as the value of the invocation.
    14. +
    15. One invocation evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 5. That invocation is waiting…
    16. +
    17. One invocation evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 4. That invocation is waiting…
    18. +
    19. One invocation evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 3. That invocation is waiting for the value of the recursive invocation of fib on the number (- n 1) = 2. When that value is eventually available, it will be stored in a temporary location and fib will be invoked recursively on the number (- n 2) = 1. When the value of the second recursive invocation of fib is eventually available, it will be added to the stored value of the first recursive invocation of fib and the result of the addition will be returned as the value of the invocation.
    20. +
    21. One invocation evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 2. That invocation has already stored in a temporary location the value of the recursive invocation of fib on the number (- n 1) = 1 and is waiting for the value of the recursive invocation of fib on the number (- n 2) = 0. When the value of the second recursive invocation of fib is eventually available, it will be added to the stored value of the first recursive invocation of fib and the result of the addition will be returned as the value of the invocation.
    22. +
    23. One invocation evaluating the body of fib with respect to a lexical environment binding, in the value namespace, the variable n to the number 0. That invocation directly returns the number 0 as its value without any further recursive invocation of fib.

    The values of the Fibonacci sequence can also be computed by the global function fib-iter:

    > (fdef fib-iter (n)
    (fib-iter-internal n 0 1))
    fib-iter

    > (fdef fib-iter-internal (n a b)
    (if (= n 0)
    a
    (if (= n 1)
    b
    (fib-iter-internal (- n 1) b (+ a b)))))
    fib-iter-internal

    > (fib-iter 10)
    55

    The bulk of the work is done by the auxiliary global function fib-iter-internal, which contains one recursive function call. The call to fib-iter-internal in fib-iter and the recursive function call in fib-iter-internal are both in tail position.

    Whereas the global function fib computes the Fibonacci sequence top-down in a branching process that repeats the computation of some Fibonacci numbers, the global function fib-iter computes the Fibonacci sequence bottom-up in a linear process that do not repeat the computation of any Fibonacci numbers:

    -

    Let ninit be the the argument of fib-iter and n, a, and b be the arguments of the invocation of fib-iter-internal under consideration. When processing the non-recursive invocation of fib-iter-internal, n = ninit, a = F0, and b = F1. If ninit = 0, then fib-iter-internal returns a = F0. Otherwise, if ninit = 1, then fib-iter-internal returns b = F1. Otherwise, fib-iter-internal invokes itself recursively. On each recursive invocation of fib-iter-internal, n is decremented by 1, a is replaced by the next Fibonacci number in the sequence F0, F1, F2, …, and b is replaced by the next Fibonacci number in the sequence F1, F2, F3, …. When processing the (ninit - 1)-th recursive invocation of fib-iter-internal, n = ninit - (ninit - 1) = 1, a = Fninit - 1, and b = Fninit and fib-iter-internal returns b = Fninit.

    +

    Let $n_\mathrm{init}$ be the the argument of fib-iter and $n$, $a$, and $b$ be the arguments of the invocation of fib-iter-internal under consideration. When processing the invocation of fib-iter-internal from fib-iter, $n=n_\mathrm{init}$, $a=F_0$, and $b=F_1$. If $n_\mathrm{init}=0$, then fib-iter-internal returns $a=F_0$. Otherwise, if $n_\mathrm{init}=1$, then fib-iter-internal returns $b=F_1$. Otherwise, fib-iter-internal invokes itself recursively. On each recursive invocation of fib-iter-internal, $n$ is decremented by $1$, $a$ is replaced by the next Fibonacci number in the sequence $F_0$, $F_1$, $F_2$, …, and $b$ is replaced by the next Fibonacci number in the sequence $F_1$, $F_2$, $F_3$, … When processing the $(n_\mathrm{init}-1)$-th recursive invocation of fib-iter-internal, $n=n_\mathrm{init}-(n_\mathrm{init}-1)=1$, $a=F_{0+n_\mathrm{init}-1}=F_{n_\mathrm{init}-1}$, and $b=F_{1+n_\mathrm{init}-1}=F_{n_\mathrm{init}}$ and fib-iter-internal returns $b=F_{n_\mathrm{init}}$.

    Here is an invocation trace of the evaluation of the form (fib-iter 6):

    -

    When the evaluator is processing the invocation of fib-iter-internal on the numbers 1, 5, and 8, there are 7 active invocations (1 of fib-iter and 6 fib-iter-internal):

    +

    When the evaluator is processing the invocation of fib-iter-internal on the numbers 1, 5, and 8, there are $7$ active invocations ($1$ of fib-iter and $6$ of fib-iter-internal):

      -
    1. One evaluating the body of fib-iter with respect to a lexical environment binding, in the value namespace, the variable n to the number 6. That invocation is waiting for the value of the invocation of fib-iter-internal on the numbers n = 6, 0 and 1. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    2. -
    3. One evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 6, the variable a to the number 0, and the variable b to the number 1. That invocation is waiting for the value of the recursive invocation of fib-iter-internal on the numbers (- n 1) = 5, b = 1, and (+ a b) = 1. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    4. -
    5. One evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 5, the variable a to the number 1, and the variable b to the number 1. That invocation is waiting…
    6. -
    7. One evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 4, the variable a to the number 1, and the variable b to the number 2. That invocation is waiting…
    8. -
    9. One evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 3, the variable a to the number 2, and the variable b to the number 3. That invocation is waiting…
    10. -
    11. One evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 2, the variable a to the number 3, and the variable b to the number 5. That invocation is waiting for the value of the recursive invocation of fib-iter-internal on the numbers (- n 1) = 1, b = 5, and (+ a b) = 8. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    12. -
    13. One evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 1, the variable a to the number 5, and the variable 8 to the number 5. That invocation directly returns the number b = 8 as its value without any further invocation of fib-iter-internal.
    14. +
    15. One invocation evaluating the body of fib-iter with respect to a lexical environment binding, in the value namespace, the variable n to the number 6. That invocation is waiting for the value of the invocation of fib-iter-internal on the numbers n = 6, 0 and 1. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    16. +
    17. One invocation evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 6, the variable a to the number 0, and the variable b to the number 1. That invocation is waiting for the value of the recursive invocation of fib-iter-internal on the numbers (- n 1) = 5, b = 1, and (+ a b) = 1. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    18. +
    19. One invocation evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 5, the variable a to the number 1, and the variable b to the number 1. That invocation is waiting…
    20. +
    21. One invocation evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 4, the variable a to the number 1, and the variable b to the number 2. That invocation is waiting…
    22. +
    23. One invocation evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 3, the variable a to the number 2, and the variable b to the number 3. That invocation is waiting…
    24. +
    25. One invocation evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 2, the variable a to the number 3, and the variable b to the number 5. That invocation is waiting for the value of the recursive invocation of fib-iter-internal on the numbers (- n 1) = 1, b = 5, and (+ a b) = 8. When that value is eventually available, it will be returned as the value of the invocation without any further processing.
    26. +
    27. One invocation evaluating the body of fib-iter-internal with respect to a lexical environment binding, in the value namespace, the variable n to the number 1, the variable a to the number 5, and the variable 8 to the number 5. That invocation directly returns the number b = 8 as its value without any further recursive invocation of fib-iter-internal.
    diff --git a/system-files/USER-MANUAL b/system-files/USER-MANUAL index 5812ff0..4fe6e0c 100644 --- a/system-files/USER-MANUAL +++ b/system-files/USER-MANUAL @@ -17,13 +17,13 @@

    This section provides an overview of the programming language. Some of the concepts introduced in this section will be illustrated in the section Listener Buffers. An introduction to writing programs can be found in the tutorial. A detailed account of the programming language can be found in the reference manual.

    The programming language, which is called EVLambda like the project, is heavily inspired by the programming languages Scheme and Common Lisp. The bibliography contains many references covering and/or using those two programming languages.

    In EVLambda, there is no difference of nature between code and data. Both code and data are represented by objects and it is the context of its occurrence that determines if an object must be treated as code or as data. The word “object” is used here in the broad sense of data structure without any reference to object-oriented programming.

    -

    As we will see below, objects are patterns of bits inside the computer's memory. To facilitate communicating about objects, reading and writing code and data, etc., objects have associated sequences of characters that can be used to represent them.

    +

    As we will see later in this section, objects are patterns of bits inside the computer's memory. To facilitate communicating about objects, reading and writing code and data, etc., objects have associated sequences of characters that can be used to represent them.

    Most objects have at least one readable representation. A readable representation of an object is a sequence of characters that can be used to represent the object in input operations. The reader is the component of the programming language responsible for converting a readable representation into the corresponding object.

    All objects have exactly one printable representation. The printable representation of an object is a sequence of characters that can be used to represent the object in output operations. When an object has exactly one readable representation, its printable representation is identical to its readable representation. When an object has more than one readable representation, its printable representation is identical to one of its readable representations chosen to be the standard way to represent the object. When an object has no readable representations, its printable representation is a sequence of characters revealing its type. The printer is the component of the programming language responsible for converting an object into its printable representation.

    -

    Objects are organized into classes called types and types are organized into a hierarchy of types. The type at the top of the hierarchy is called the root type. The types at the bottom of the hierarchy are called the leaf types. If type A is located below type B in the hierarchy, then type A is called a subtype of type B and any object that belongs to type A also belongs to type B. The term “data type” is sometimes used in place of the term “type” even though a (data) type is a class of objects and an object can be a piece of code or a piece of data.

    +

    Objects are organized into classes called types and types are organized into a hierarchy of types. The type at the top of the hierarchy is called the root type. The types at the bottom of the hierarchy are called the leaf types. Any object belongs to exactly one leaf type. If type $A$ is located below type $B$ in the hierarchy, then type $A$ is called a subtype of type $B$ and any object that belongs to type $A$ also belongs to type $B$. The term “data type” is sometimes used in place of the term “type” even though a (data) type is a class of objects and an object can be a piece of code or a piece of data.

    Here is a tree-view representation of the hierarchy of types:

    object
    |-void
    |-boolean
    |-number
    |-character
    |-string
    |-symbol
    | |-keyword
    | |-variable
    |-list
    | |-empty-list
    | |-cons
    |-vector
    |-function
    | |-primitive-function
    | |-closure
    -

    And here is a brief description of the leaf types (readable and printable representations have a gray background):

    +

    Here is a brief description of the leaf types (readable and printable representations have a gray background):

    void
    There is exactly one object of type void. Its readable representation is #v and its purpose is to represent missing or undefined objects.
    @@ -32,9 +32,9 @@
    number
    Numbers represent mathematical numbers: 123, -123, 123.456, -123.456, … The representation of a mathematical number by an object of type number can be exact or approximate.
    character
    -
    Characters represent Unicode characters: #\a, #\b, #\c, … Most of the characters used in the world have a corresponding Unicode character.
    +
    Characters represent Unicode characters: #"a", #"b", #"c", #"ä½ ", #"好", … Most of the characters used in the world have a corresponding Unicode character.
    string
    -
    Strings represent indexed sequences of Unicode characters: "abc", …
    +
    Strings represent indexed sequences of Unicode characters: "abc", "你好", …
    keyword
    Keywords can, among other uses, represent named values: :red, :green, :blue, …
    variable
    @@ -42,7 +42,7 @@
    empty-list
    There is exactly one object of type empty-list. Its readable representation is () and its purpose is to represent empty lists of objects.
    cons
    -
    A cons is a pair of objects. The first element is called the car of the cons and the second element is called the cdr of the cons. Conses are the building blocks of many data structures. In particular, conses can be chained together to represent non-empty lists of objects. A non-empty list of objects is represented by a cons whose car is the first element of the list and whose cdr is the sublist of the list obtained by omitting its first element. For example, the list (1 2 3) is represented by a chain of three conses: a first cons whose car is the number 1 and whose cdr is the second cons, a second cons whose car is the number 2 and whose cdr is the third cons, and a third cons whose car is the number 3 and whose cdr is the empty list.
    +
    A cons is an ordered pair of objects. The first element is called the car of the cons and the second element is called the cdr of the cons. Conses are the building blocks of many data structures. In particular, conses can be chained together to represent nonempty lists of objects. A nonempty list of objects is represented by a cons whose car is the first element of the list and whose cdr is the sublist of the list obtained by omitting its first element. For example, the list (1 2 3) is represented by a chain of three conses: a first cons whose car is the number 1 and whose cdr is the second cons, a second cons whose car is the number 2 and whose cdr is the third cons, and a third cons whose car is the number 3 and whose cdr is the empty list.
    vector
    Vectors represent indexed sequences of objects: #(), #(1 2 3), …
    primitive-function
    @@ -50,30 +50,30 @@
    closure
    Closures are input/output mappings implemented in EVLambda. Some closures are tagged as being a macro. Macros are code to code mappings used to create new language constructs. Closures have no readable representations and their printable representation is #<closure>.
    -

    Each symbol has an associated sequence of characters (called the name of the symbol) that serves as its readable representation. If the name of a symbol starts with a colon, then the symbol is a keyword. Otherwise, the symbol is a variable. The reader always converts the same name into the same symbol.

    +

    Each symbol has an associated sequence of characters (called the name of the symbol) that serves as its readable representation. If the name of a symbol starts with a colon, then the symbol is a keyword. Otherwise, the symbol is a variable. The reader always converts the same name into the same symbol. To that end, the reader maintains two mappings from names to symbols called packages: one package mapping the names of the previously encountered variables to the corresponding variables and one package mapping the names of the previously encountered keywords to the corresponding keywords.

    The printable representation of a list consists of a left parenthesis followed by the printable representations of the elements of the list separated by a single space followed by a right parenthesis. The printable representation of a list is just one of its infinitely many readable representations. In particular, other readable representations can be obtained by using sequences of at least one whitespace character instead of single spaces to separate the elements of the list.

    -

    Although a macro is technically a function (because a macro is an object of type closure and type closure is a subtype of type function), the word “function” is sometimes used to denote specifically a function other than a macro (that is, a primitive function or a closure not tagged as being a macro).

    +

    Although a macro is technically a function (because a macro is an object of type closure and type closure is a subtype of type function), the word “function” is often used to denote specifically a function other than a macro (that is, a primitive function or a closure not tagged as being a macro).

    Variables name objects through the use of namespaces, bindings, environments, and lookup rules:

    -

    A variable that is associated with an object through a binding belonging to namespace X of environment Y is said to be bound to the object in namespace X of environment Y. A variable that is not associated with an object through a binding belonging to namespace X of environment Y is said to be unbound in namespace X of environment Y.

    -

    Objects, bindings, and environments are represented by non-overlapping patterns of bits located inside a region of the computer's memory called the heap. Each object, binding, and environment is uniquely identified by the address of the pattern of bits that represents it. Like objects, bindings, and environments, addresses are also represented by patterns of bits. A reference to an object, binding, or environment is an instance of the pattern of bits that represents the address of the pattern of bits that represents the object, binding, or environment. By abuse of language, we often confuse a reference to an object, binding, or environment with the referenced object, binding, or environment itself.

    -

    The pattern of bits representing an object has two parts: one part specifying the type of the object and one part specifying which member of the type is represented by the object.

    +

    A variable that is associated with an object through a binding belonging to namespace $X$ of environment $Y$ is said to be bound to the object in namespace $X$ of environment $Y$. A variable that is not associated with an object through a binding belonging to namespace $X$ of environment $Y$ is said to be unbound in namespace $X$ of environment $Y$.

    +

    Objects, bindings, and environments are represented by nonoverlapping patterns of bits located inside a region of the computer's memory called the heap. Each object, binding, or environment is uniquely identified by the address of the pattern of bits that represents it. Like objects, bindings, and environments, addresses are also represented by patterns of bits. A reference to an object, binding, or environment is an instance of the pattern of bits that represents the address of the pattern of bits that represents the object, binding, or environment. By abuse of language, we often confuse a reference to an object, binding, or environment with the object, binding, or environment being referenced.

    +

    The pattern of bits representing an object has two parts: one part specifying the type of the object and one part specifying which member of the type the object is.

    An object, binding, or environment references another object, binding, or environment by embedding into its representation a reference to that other object, binding, or environment:

    -

    The references embedded into the representation of an object, binding, or environment can be thought of as occupying memory locations denoted by the object, binding, or environment. By abuse of language, we often say that a memory location contains an object, binding, or environment when in reality the memory location contains a reference to the object, binding, or environment.

    -

    Multiple objects, bindings, or environments can reference a common object, binding, or environment, leading to the sharing of the common object, binding, or environment. An object, binding, or environment can reference itself directly (XX) or indirectly (XY→…→X), leading to the existence of a cycle.

    -

    Objects of type void, boolean, keyword, symbol, or empty-list have the following uniqueness properties:

    +

    The references embedded into the representation of an object, binding, or environment can be thought of as occupying memory locations denoted by the object, binding, or environment. For example, a cons can be thought of as denoting two memory locations: one containing a reference to the car of the cons and one containing a reference to the cdr of the cons. By abuse of language, we often say that a memory location contains an object, binding, or environment when in reality the memory location contains a reference to the object, binding, or environment.

    +

    Multiple objects, bindings, and environments can reference a common object, binding, or environment, leading to the sharing of the common object, binding, or environment. An object, binding, or environment can reference itself directly ($X\rightarrow X$) or indirectly ($X\rightarrow Y\rightarrow\cdots\rightarrow X$), leading to the existence of a cycle.

    +

    Objects of type void, boolean, keyword, symbol, and empty-list have the following uniqueness properties:

    -

    Objects of type integer, character, or string do not have similar uniqueness properties:

    +

    Objects of type integer, character, and string do not have similar uniqueness properties:

    -

    Objects of type void, boolean, number, character, string, keyword, symbol, empty-list, primitive-function, or closure are immutable and cannot be altered. Objects of type cons or vector, bindings, and environments are mutable and can be altered in the following ways:

    +

    Objects of type void, boolean, number, character, string, keyword, symbol, empty-list, primitive-function, and closure are immutable and cannot be altered. Objects of type cons and vector, bindings, and environments are mutable and can be altered in the following ways:

    -

    Replacing an object by another object can be thought of as replacing the object reference contained in a memory location by another object reference.

    +

    (As explained above, what replacing an object by another object really means is replacing a reference to an object by a reference to another object.)

    The life cycle of an object, binding, or environment consists of the following events: a creation (which consists of an allocation followed by an initialization) followed by any number of alterations followed by a destruction (which consists of a deallocation). Alterations are possible only if the object, binding, or environment is mutable.

    The destruction of an object, binding, or environment occurs automatically if and when the object, binding, or environment becomes unreachable. The rules used to determine if an object, binding, or environment is reachable are as follows (the concepts of global environment and control stack will be introduced later in this section):

    -

    The fact that a global/lexical/dynamic environment contains bindings with such scope and such extent is a consequence of the evaluation rules stated later in this section. It is thus not necessary to know the concepts of scope and extent to start writing programs in EVLambda. Knowing the evaluation rules should be enough.

    +

    The fact that a global/lexical/dynamic environment contains bindings with such scope and such extent is a direct consequence of the evaluation rules stated later in this section. It is thus not necessary to know the concepts of scope and extent to start writing programs in EVLambda. Knowing the evaluation rules should be enough.

    Evaluations are all done with respect to the same global environment. That environment, referred to as “the global environment”, is created when the evaluator starts and continues to exist until the evaluator stops. The global environment of an evaluator that has just started contains a set of predefined bindings, most of which providing access to a primitive function. As forms are evaluated, the global environment can change in the following ways: