SketchyLISP Reference Manual - Copyright (C) 2005 Nils M Holm
Previous: Programs | - Index - | Next: Primitive Functions |
Each SketchyLISP expression may be reduced using the following rules. If more than one reduction is possible, the order of application is call-by-value, ie inner expressions are evaluated first. When no rules can be applied to an expression, that expression is said to be in its normal form.
The arrow operator
x => y
means x reduces to y. The notation
eval[x]
is used to denote the normal form of x. The operator
s[x/y]
denotes the substitution of each x in s by y.
In each step, the resulting expression is different from the original one. Expressions never get changed in SketchyLISP, but only rewritten. This process is similar to rewriting a term by hand: adding a new line at the bottom does not change any of the above lines.
Symbols reduce to the expressions they are bound to. Bindings of Symbols to expressions are created by the define, lambda, and letrec* pseudo functions, which will be explained later. Symbols which have not explicitly been bound to any expressions, are by convention bound to ().
Booleans, Numbers, and Procedures are always in their normal forms.
The first member of each List is reduced to its normal form. If the normal form of the first member is not one of these Symbols
cond, define, lambda, letrec*, quote
then the remaining members are also reduced to their normal forms.
Formally: if eval[a1] is not in {cond, define, lambda, letrec*, quote}, then
(a1 a2 ... aN) => (eval[a1] eval[a2] ... eval[aN])
and otherwise
(a1 a2 ... aN) => (eval[a1] a2 ... aN)
In other words, arguments of the above functions are passed by name, while all other arguments are passed by value.
Subsequent steps will be performed with the above reductions in effect.
If the first member of a List is a Symbol which denotes a primitive operation of SketchyLISP, this operation is applied to the remaining members of the List.
(primitive a2 ... aN) => primitive(a2, ..., aN)
where primitive(...) denotes the application of a built-in function of the interpreter. (For a summary of primitive functions see a later section.)
A lambda function is an expression of the form
(lambda (x) t)
(X) is called the argument list of the lambda function, x is an argument (or a variable) of the function, and t is the term (or body) of the function.
If the first member of a List is a lambda function, then the List is reduced in two steps. In the first step, the expression of the form
((lambda (x) t) y)
(which denotes an application of a lambda function to the argument y) is reduced by substituting each free x that occurs in t by y, resulting in t':
((lambda (x) t) y) => t[x/y] = t'
A Symbol x is free in a term t, if
For example, x is free in
(lambda (y) x)
but is is not free in
(lambda (x) x)
A Variable that is not free in an expression is said to be bound in that expression. Note especially that x is bound in (lambda (x) t) but free in the t of (lambda (x) t).
Since a lambda expression may have more than a single argument, the more general form of its reduction is
((lambda (x1 ... xN) t) y1 ... yM) => t[x1/y1] ... [xN/yM] = t'
The number of the formal arguments of lambda (n) must exactly match the number of actual arguments (m) in function applications. If the numbers do not match, the normal form of the expression is undefined.
In the second step, the resulting expression t' is reduced to its normal form using any reduction rule described in this section.
t' => eval[t'] = t''
The result of this step is the result of the entire reduction. The entire reduction may be written as:
((lambda (x1 ... xN) t) y1 ... yN) => eval[ t[x1/y1] ... [xN/yN] ]
If the formal argument list of lambda is an Improper List, then the function expects at least one actual argument for each formal argument preceding the dot of the improper argument list. For example, the following lambda function accepts one or more (actual) arguments:
(lambda (a . b) x)
This function expects at least three arguments:
(lambda (a b c . d) x)
The symbols preceding the dot are bound as described in the previous section. The argument following the dot is bound to the rest of the list of actual arguments. The rest of this list is the list of actual arguments after removing each member that already has been bound to a symbol:
((lambda (a . b) b) 'foo 'bar) => '(bar) ((lambda (a . b) b) 'foo 'bar 'baz) => '(bar baz)
If there are no remaining members, the symbol after the dot is bound to ().
((lambda (a . b) b) 'foo) => '()
The empty List () is always in its normal form:
() => ()
Note: R5RS Scheme requires empty lists to be quoted. In SketchyLISP quotation of empty lists is optional.
If the first member of a List is neither a Symbol representing a primitive function nor a lambda function, the normal form of the List is undefined.
(() a2 ... aN) => undefined (undefined a2 ... aN) => undefined
Previous: Programs | - Index - | Next: Primitive Functions |