Quotations
In Smojo, a quotation defines a word with no name. When a quotation is run, it produces an XT with no name ( that is no named entry in the Dictionary. ) Such nameless XTs produced from Quotations are called closures. However, apart from the "namelessness", there is absolutely no difference between closures and ordinary XTs. As you will see later, Quotations are extremely useful in Smojo programming.
Word | Action |
---|---|
:> and ; |
These words define a quotation in interpretation mode only, resulting in a closure (i.e., an XT) on the stack. |
[: and ;] |
These words also define a quotation but are only applicable in compilation mode. When the enclosing word is run, a closure is created on the stack. |
EXECUTE |
This word will run any XT on the stack, whether closures created from quotations or XTs of words obtained by ticking. |
Some Examples
Quotations in Interpretation Mode.:> "I have no name!" . ; execute I have no name! okIn this example, the closure is pushed onto the stack.
EXECUTEpops it from the stack and runs it. Quotations in Compilation Mode.
: hello [: "I have a name!" . ;] ; hello .S execute <1> com.terraweather.mini.XT@77ff2061 I have a name! okCompared to the previous Example, this closure is only created and pushed onto the stack when the enclosing word
HELLOis run.
Lexical Closures
Take a hard look at this word:
: div? ( n -- xt ) { n } [: n mod 0 = ;] ;
DIV?obviously encloses a Quotation. This quotation defines a word that checks if a number on the stack is divisible by N, which is determined at the time the closure is created. For example:
32 div? decompile [0] Literal<32> [1] MOD [2] Literal<0> [3] = okThe decompilation shows that the code section of the closure is
32 MOD 0 =. Notice that the number (N=32) has been compiled into the closure as a literal. These types of closure are historically known as "lexical closures". All closures in Smojo are "lexical". ( The alternative is a situation where the determination of the value of N is delayed to the runtime environment of the closure. Such closures are "dynamic". It is easily possible to create dynamic closures in Smojo, but historically this is known to lead to hard-to-debug code. )
Key idea: Smojo will always automatically replace locals defined in the enclosing environment as Literals when it creates closures. To see this try
1749 div? decompile
Functional Composition
Quotations are often used to create new functionality via composition at runtime (vs doing this statically at compilation time). A simple example:
: ** ( xt xt -- xt ) { f g } [: f execute g execute ;] ; 0 ' sin ' cos ** execute . 1.0 ok
**takes two XTs and returns a new XT, resulting in a composition function. Mathematically, the example is just: g(f(x)) = cos(sin(x)) A better and more efficient way to write
**is using the word
~which converts an XT into an executable local:
: ** ( xt xt -- xt ) ~ { g } ~ { f } [: f g ;] ; 0 ' sin ' cos ** execute . 1.0 ok
Quiz
Question 1
In the last example, use decompilation to compare the closures from the first and second version of**. Which is more efficient? Why?
: ** ( xt xt -- xt ) { f g } [: f execute g execute ;] ; 0 ' sin ' cos ** decompile : ** ( xt xt -- xt ) ~ { g } ~ { f } [: f g ;] ; 0 ' sin ' cos ** decompile [0] Literal XT(20d95360) [1] EXECUTE [2] Literal XT(299404ea) [3] EXECUTE ** redefined [0] SIN [1] COS okThe first version puts the XTs of
SINand
COSon the stack then execute them. The second version directly execute the functions so is more efficient.
Question 2
Can ordinary locals be used within a quotation? Prove your answer with a program.
:> { n } n . ; \ locals be used within a quotation 5 swap execute \ 5
Question 3*
Write a wordDX ( xt -- xt ), which given a mathematical function, f(x) returns another function which approximates its derivative, df/dx. Hint: you can use the simple Euler method.
Question 4
Write a wordDX2 ( xt -- xt )which approximates the second derivative, d2f/dx2. Can you re-use your answer to Question 3?
0.0000001 constant DELTA : DX ( xt -- xt ) ~ { f } [: { x } x DELTA +. f x DELTA -. f -. DELTA 2 *. /. ;] ; : DX2 ( xt -- xt ) DX DX ; : f sin ; #pi 3 *. 2 /. constant 3pi/2 3pi/2 f . cr \ sin 3pi/2 = -1 3pi/2 ' f DX execute . cr \ cos 3pi/2 = 0 3pi/2 ' f DX2 execute . cr \ -sin 3pi/2 = 1The output may not be exact because of the approximation error of simple Euler method.
Question 5**
We could have designed the wordDXto take both a number x0 and a function to give the value of the derivative at x0. That is,
DX ( n xt -- n ). Compare in detail this design choice to the one taken in Questions 3 and 4.
DXis not composable in this case. We cannot write
: DX2 DX DX ;if
DXis defined as
( n xt -- n ).