News

muvee Reveal - the latest incarnation of muvee's flagship product - has just been released on 11 June 2008! The behaviours of the 8 bundled styles are specified using muSE, in addition to all the styles developed for the now discontinued muvee autoProducer 6.1.

Friday, January 19, 2007

A simple module system using macros

In this post, I describe how to build a simple module system. For simplicity, I assume we won't be providing support for mutually recursive functions within a module.

You can think of a module as a set of symbols and the values they must be bound to. These bindings are made current whenever you "enter" a module's scope and they vanish whenever you exit the scope. Here's what we hope to achieve, with a statistics module as an example -


(define Statistics
(module
(mean
(fn args (/ (apply + args) (length args))))

(variance
(fn args (- (apply mean (map (fn (x) (* x x)) args))
(let ((mx (apply mean args)))
(* mx mx)))))
))

The above module provides definitions for mean and variance.

Notice that the body of the module definition is very similar to the bindings section of the let construct. We can therefore use the let construct in a macro system to implement module as a macro.

It is simple to use the Statistics module like this -
{Statistics (mean 1 2 3)}
. For that to be possible, Statistics must itself be a macro. Therefore, module must be a macro that generates a macro. Here is a definition for module -

(define module
(fn 'bindings
(let ((evaluated-bindings
(eval (list 'let bindings
(list 'map
(fn: ((s . _))
(list s (cons 'quote (eval s))))
(cons 'quote bindings))))))
(fn 'args
(cons 'let (cons evaluated-bindings args))))))

Now all you have to do to rope in the Statistics definitions in a block of code is to wrap it up in a {Statistics ...} block.

Suppose we wish to use the module name to resolve the value of a single symbol. It is potentially expensive to do ({Statistics mean} 1 2 3) because the brace expression places a (potentially huge) let block there which gets evaluated every time the whole expression is evaluated. We can make this case more efficient by defining a scope resolution operator like this -

(define :: (fn 'args (cons quote (eval (eval args)))))

Then we can resolve the meaning of mean like this
({:: Statistics mean} 1 2 3)
. The resolution will happen once at read time, after which the actual function will be used.

See examples/modules.scm.

Thursday, January 18, 2007

"Processes" branch is now the "trunk".

The experimental multi-processing support that was being developed in the processes branch is now merged on to the trunk (v140). That is, its official! I've been waiting for this for a long time now. Apart from various bug fixes and other process related stuff already described, here's whats new -

  1. New primitives post and process?.
  2. New s-expr syntax and functions to work with raw byte data.
  3. MAJOR code change to make muSE truly embeddable. This change breaks API compatibility. The new API calls all take the environment pointer as their first arguments so that multiple muSE environments can coexist in the same OS process or thread without interfering with each other.

Wednesday, January 10, 2007

Specifying function arguments using key-value pairs

Since v129 in the processes branch, two new primitives call/keywords and apply/keywords have been added to let function argument be specified in an order that's different from the declaration order. These primitives do not change the evaluation complexity of functions at all, but simply expose a function's declared arguments as "keywords".


(call/keywords f 'key1 val1 'key2 val2 ...)

Evaluates the given function by explicitly binding each named argument to the given value. Can also be used on macros, in which case the value positions are used without being evaluated. This is the counterpart of the normal position based evaluation.

(apply/keywords f alist)

If you store a set of argument bindings in an alist, you can apply a function to those bindings using apply/keywords. This is the counterpart of the position-based apply.


Any arguments that don't feature in the supplied key-value pairs take on their current value in the environment. This lets you specify common arguments using just a let binding or a global define. Such common arguments are therefore said to be implemented via a limited form of dynamic scoping.