Core language


Functions

muse_cell fn_quote (muse_env *env, void *context, muse_cell args)
 Quotes the given arguments without evaluating them.
muse_cell fn_cons (muse_env *env, void *context, muse_cell args)
 (cons head tail).
muse_cell syntax_lambda (muse_env *env, void *context, muse_cell args)
 (fn formal-args <body>).
muse_cell syntax_block (muse_env *env, void *context, muse_cell args)
 (fn: (arg1 arg2 --- argN) ---body---).
muse_cell syntax_let (muse_env *env, void *context, muse_cell args)
 (let <variable-bindings> <body>).
muse_cell syntax_case (muse_env *env, void *context, muse_cell args)
 (case object <match-cases>).
muse_cell fn_apply (muse_env *env, void *context, muse_cell args)
 (apply fn arglist).
muse_cell fn_eval (muse_env *env, void *context, muse_cell args)
 (eval s-expr).
muse_cell fn_callcc (muse_env *env, void *context, muse_cell args)
 (call/cc (fn (k) --- (k result) ---)).

Function Documentation

muse_cell fn_quote ( muse_env env,
void *  context,
muse_cell  args 
)

Quotes the given arguments without evaluating them.

For example,

 (quote . hello) 
is identical to
 'hello 

muse_cell fn_cons ( muse_env env,
void *  context,
muse_cell  args 
)

(cons head tail).

Creates a new cons cell with the given head and tail. If no free cells are available, invokes the garbage collector and grows the heap if necessary.

See also:
muse_cons()

muse_cell syntax_lambda ( muse_env env,
void *  context,
muse_cell  args 
)

(fn formal-args <body>).

Common syntax -

 (fn (x1 x2 ... xN)
   expr1
   expr2
   ...
   result-expr)

fn creates a closure when it is executed. A closure is a copy of the body of the lambda with all non-parameter variables bound to their current values. If an undefined symbol (free variable) is used in the body, it will simply evaluate to itself. Once the symbol is defined after the creation of the closure, the defined value will take the place of the symbol in the closure.

For example -

 (define norm3 (fn (x y z) (sqrt (+ (* x x) (* y y) (* z z)))))
defines the symbol norm3 to be a function that computes the norm (i.e. vector-length) of a 3-vector. You use the function in expressions like the following -
 (norm3 3 4 0)
 (norm3 10 20 30)

Binding formals -

Argments to a function are given as a list. Therefore a function may accept a variable number of arguments, computing different (but hopefully related) values in the different cases. The mechanism using which a function accesses its entire argument list is more general than purely for that case, though.

The arguments to the norm3 function in the above examples may be considered to be lists like

 (10 20 30) 
. In order to obtain the argument list, simply drop the function position. It is now easy to see that the formal argument pattern
 (x y z) 
is similar to the given argument list and a one-to-one mapping can be established between the symbols x, y and z in the formals list and the values in the arguments list. This is how the matching and binding is done.

In order to illustrate the matching method, let us write the norm3 argument list in its canonical form - as

 (10 . (20 . (30 . ()))) 
. The formals pattern can also be written in such a canonical form - as
 (x . (y . (z . ()))) 
.

Note:
The binder lets a symbol to be bound to either the head or the tail of any cons cell in the argument list. Anything other than a symbol in the formals pattern must match the argument position exactly.
Therefore if you simply use one symbol as the formals specification (ex: args), it will be bound to the entire argument list. If you use a pattern like
 (x . xs) 
, x will be bound to the first argument 10 and xs will be bound to the first cons cell's tail, i.e. the list
 (20 30) 
. It is valid for a symbol to be bound to (), but it is not valid to deconstruct a () value and assign its components to symbols. i.e. () will not match against (x . xs) but will match against xs.

See also:
muse_bind_formals()

syntax_let

syntax_case

muse_cell syntax_block ( muse_env env,
void *  context,
muse_cell  args 
)

(fn: (arg1 arg2 --- argN) ---body---).

Very similar to syntax_lambda, except that it doesn't create a closure. All the free variables in the block (i.e. those except the given arguments) are expected to be bound by the environment in which the block is invoked. Otherwise a block is identical to syntax_lambda and can be used in all places a closure can be used.

In particular, a block is effective with call/cc to specify jump out points such as exceptions and loop breaks. A normal closure can be used as well, but the closure will be repeatedly created every time the call/cc expression is evaluated.

Creating a block is extremely cheap compared to creating a closure using syntax_lambda. As a thumb rule, blocks are equivalent to closures when they are consumed within the scope of their declaration - as in the following example -

    (map (fn: (x) (* x 2)) '(1 2 3 4 5))
The above code evaluates to the list
    (2 4 6 8 10)
and fn: in this case is equivalent to using fn.

If you define a symbol to a block, then you need to be careful, because the value of any free variables (non-local) used within the block can be redefined outside the block. For example -

   (define y 2)
   (define double (fn: (x) (* x y)))
   (double 3)
       -> 6
   (double 5)
       -> 10
   (define y 3)
   (double 3)
       -> 9
   (define y 100)
   (double 3)
       -> 300
whereas the same sequence with "fn" instead will be -
   (define y 2)
   (define double (fn (x) (* x y)))
   (double 3)
       -> 6
   (double 5)
       -> 10
   (define y 3)
   (double 3)
       -> 6
   (define y 100)
   (double 3)
       -> 6
because fn "captures" the value of y at the time it is invoked to create a closure. It is the closure thus created which is bound to the symbol "double".

muse_cell syntax_let ( muse_env env,
void *  context,
muse_cell  args 
)

(let <variable-bindings> <body>).

Syntax -

 (let ((pattern1 value1)
       (pattern2 value2)
       ...
       (patternN valueN))
   expr1
   expr2
   ...
   result-expr)

let introduces local variables bound to the results of given expressions in the context of a block of code. For example, an expression to compute the distance between two points (x1,y1) and (x2,y2) can be written as -

 (let ((dx (- x2 x1))
       (dy (- y2 y1)))
   (print "Computing distance between two points ...")
   (sqrt (* dx dx) (* dy dy)))
The above code introduces the symbols dx and dy bound to x2-x1 and y2-y1 respectively, in the expression (sqrt (* dx dx) (* dy dy)).

The result of the let expression is the result of the last expression in the body. In the above example, the last statement computes the sqrt.

The variable binding scheme in let is exactly the same as the argument binding scheme for lambdas. This means, you can decompose lists using let expressions as follows -

 (let (((x y . xs) things))
    (print "x = " x ", y = " y ", and the rest are " xs))
If things is the list (1 2 3 4), the above expression will print
 x = 1, y = 2, and the rest are (3 4)

If any of the binding operations failed, the let block is not evaluated and the result is ().

See also:
muse_bind_formals()

syntax_lambda

syntax_case

muse_cell syntax_case ( muse_env env,
void *  context,
muse_cell  args 
)

(case object <match-cases>).

Syntax -

 (case object
       (match-expr1 result1)
       (match-expr2 result2)
       ...
       (T else-result))
Like switch in C, but way more expressive. The result of a case expression is the result corresponding to the match-expr that succeeded in a match-bind operation against the given object.

In the simplest case, when object is a symbol, you can use case like switch as follows -

 (case object
       ('one "One a penny")
       ('two "Two a penny")
       ('what "Hot cross buns"))

The match expressions are not limited to constants and are basically the same as the binding expressions for let or lambda. For example, here is a case expression to print the head of a list -

 (case list-object
       ((x . xs) (print "Head of " list-object " is " x "."))
       (() (print "Cannot take head of the empty list!")))
In the above code, the first match expression (x . xs) is bound against the given list-object. If list-object is a non-empty list, this bind operation will succeed and x will be bound to the head of the list and xs will be bound to the tail of the list. The second match expression is the empty list which will succeed in matching against another empty list only. So if list-object is the empty list, then the second print statement will be evaluated.

See also:
muse_bind_formals()

syntax_lambda

syntax_let

muse_cell fn_apply ( muse_env env,
void *  context,
muse_cell  args 
)

(apply fn arglist).

Equivalent to

 (eval (cons fn arglist)) 
. The apply function lets you apply the given function to the given argument list, both of which may be values of other expressions.

For example -

 (define one-to-ten '(1 2 3 4 5 6 7 8 9 10))
 (print (apply + one-to-ten))

The above code will print the sum of numbers from 1 to 10, and is equivalent to

 (print (+ 1 2 3 4 5 6 7 8 9 10)) 
.

muse_cell fn_eval ( muse_env env,
void *  context,
muse_cell  args 
)

(eval s-expr).

Evaluates the given single s-expression and returns the result. For example,

 (eval '(+ 2 3)) 
will result in 5. In this sense, eval is the counter part of quote.

muse_cell fn_callcc ( muse_env env,
void *  context,
muse_cell  args 
)

(call/cc (fn (k) --- (k result) ---)).

Abbreviation for "call with current continuation", call/cc is an implementation of the scheme recommendation for continuation support. The first and only argument to call/cc is expected to be a function that takes a single argument called the continuation. call/cc will then call this function, supplying the current continuation as the argument.

A brief intro to continuations follows. When evaluating a sub-expression of any expression, the remainder of the computation may be thought of as a function that expects the result of the sub-expression evaluation. This "remainder of the computation" function w.r.t. the specific sub-expression is called the "continuation" at that point in the evaluation process.

The whole expression may be rewritten as a call to this continuation function with the argument as the result of the sub-expression under consideration. Note that the continuation function does not return a value to the context in which it is called. Instead, it "breaks out" of the context and pretends as though the result of the sub-expression is the argument supplied to the continuation function at invocation time.

Its time for an example - what'll the following code print? .. and then, what'll it print when bomb is defined to T instead?

 (define bomb ())
 (print (+ 1 2 (call/cc (fn (k)
                             (print "before\n")
                             (if bomb (k 0))
                             (print "after\n")
                             3))
             4 5))

When bomb is (), the

 (k 0) 
part is not evaluated due to the if condition failing. Therefore the output will be -
 before
 after
 15

When bomb is changed to T, the if block will kick in and

 (k 3) 
will be evaluated. But since continuation functions do not return to te point of invocation, but to the point of the call/cc which captured them, the
 (print "after\n") 
expression never gets evaluated and the result of the call/cc block is not 3 as one would expect, but 0, because that's the argument given to the continuation function when it is invoked! So you'll get
 before
 12
as the result printed to the screen. Note that we know that the continuation invocation did not return to its invocation point because the expression
 (print "after\n") 
did not get evaluated.

Continuations are rather powerful. The can be used to implement language constructs such as -

  1. try-catch style exception handling,
  2. breaking out of loops.
  3. suspend and resume mechanism.

In general, it should be possible to store away the continuation function for future invocation. Early on in muSE's development, only a limited implementation of call/cc was put in in order to support breaking out of loops. Later on a full implementation of continuations was added. Now, you can store away the continuation function in a variable and invoke it as many times as you need to, because the continuation captures a complete snapshot of the execution environment at the time it is created.

Todo:
The current implementation of call/cc seems to be working properly on Windows + Intel, but doesn't work correctly on PowerPC. Needs investigation.


Generated on Mon Sep 25 23:12:50 2006 for muSE by  doxygen 1.4.7