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.