As an example, we take two simple extensions: an implementation of red-black trees and a simple ``functor''-like syntax that encapsulates the operations on the rb-tree data-type. The files for an extension should reside in a separate directory, so in the case of our simple functor-implementations we have the following directory-structure:
functor.scm functor/ utils.scm
Here we put the macro-definition for define-functor in a separate file, this simplifies the use in compiled code. The code follows:
;;; functor.scm
(require 'srfi-1)
(require-for-syntax 'match)
(define-macro (define-functor name imports . body)
(let ([exports
(filter-map
(match-lambda
[('define (name . llist) . body) name]
[('define name val) name]
[_ #f] )
body) ] )
`(define (,name ,@imports)
,@body
(values ,@exports) ) ) )
and
;;; functor/utils.scm (define (instantiate-functor f . imports) (apply f imports) )
One more thing is needed: a setup specification that contains information about the files and settings of the extension. In this case we have:
;;; functor.setup
(chicken-setup (command-line-arguments)
(functor ((syntax)
(require-at-runtime (functor utils)) )
(utils ()) ) )
Now we create an extension package:
% csi -setup functor -wrap wrapping extension `functor' ... functor.setup functor.scm functor/ functor/utils.scm
This will create the compressed archive functor.egg.
A user of this package can now use csi in combination with the -setup option to extract the contents from the archive, to build its components and install it in his Chicken system for easy use.
% ls functor.egg % csi -setup functor -build extracting files from extension `functor' ... functor.setup functor.scm functor/ functor/utils.scm building extension `functor' ... backing up registry... adding entry for `/home/felix/chicken/lib/functor.setup' ... compiling: csc -s ./functor/utils.scm -o ./functor/utils.so -O1 removing temporary extension `/home/felix/chicken/lib/functor.setup' ... restoring registry
Note that functor.scm is not compiled, because it is explicitly marked as syntax, which means it is only meant to be used at compile-time, in source-form.
The extension is now installed and ready for use.
% csi -quiet
>>> (require-for-syntax 'functor)
; loading /home/felix/chicken/lib/functor.scm ...
; loading /home/felix/chicken/lib/functor/utils.so ...
>>> (define-functor adder (+)
(define (make-adder init)
(lambda (x)
(set! init (+ init x))
init) ) )
>>> (define ma (instantiate-functor adder +))
>>> (define a1 (ma 99))
>>> (a1 1)
100
>>> (a1 22)
122
>>>
The extension for red/black trees is created in much the same way:
;;; rbtree.scm
;
; taken directly from Chris Okasaki's ``Purely functional Data Structures''
(require-for-syntax 'match)
(require-for-syntax 'functor)
(define-functor rb-tree (rb<?)
(define (member? x t)
(match t
[() #f]
[(_ a y b)
(cond [(rb<? x y) (member? x a)]
[(rb<? y x) (member? x b)]
[else #t] ) ] ))
(define balance
(match-lambda
[(or ('black ('red ('red a x b) y c) z d)
('black ('red a x ('red b y c)) z d)
('black a x ('red ('red b y c) z d))
('black a x ('red b y ('red c z d))) )
`(red (black ,a ,x ,b) ,y (black ,c ,z ,d)) ]
[body body] ) )
(define (insert x s)
(define ins
(match-lambda
[() `(red () ,x ())]
[(and s (color a y b))
(cond [(rb<? x y) (balance (list color (ins a) y b))]
[(rb<? y x) (balance (list color a y (ins b)))]
[else s] ) ] ) )
(match (ins s)
[(_ a y b) `(black ,a ,y ,b)] ) ) )
The setup-specification looks like this. Since we only have a single file, we don't need to put it in a separate directory:
(chicken-setup (command-line-arguments)
(rb-tree ((options "-O2")
(file "rbtree.scm")
(require-for-syntax functor match) ) ) )
Note the properties used: (options "-O2") overrides the default compiler-options ("-O1") and we use a different name for the source file26. This extension requires the define-functor extension, we have to use require-for-syntax to make sure that the macro is included at compile-time.
The wrapping, unwrapping, building and installation works identical to the previously given example. Here is some code that uses both extensions:
% cat testrb.scm
(require 'rb-tree 'srfi-1 'extras)
(require '(functor utils))
(define-values (num-rb-member? num-rb-balance num-rb-insert)
(instantiate-functor rb-tree <) )
(randomize)
(print "building list...")
(define vals (list-tabulate 10000 (lambda (_) (random 100000))))
(print "building rb-tree...")
(define t1 '())
(for-each (lambda (n) (set! t1 (num-rb-insert n t1))) vals)
(print "building hash-table...")
(define ht (make-hash-table))
(for-each (lambda (n) (hash-table-set! ht n #t)) vals)
(print "linear list:")
(time (every (lambda (n) (memq n vals)) vals))
(print "hash-table:")
(time (every (lambda (n) (hash-table-ref ht n)) vals))
(print "rb:")
(time (every (lambda (n) (num-rb-member? n t1)) vals))
% csc testrb.scm -O2
% testrb
building list...
building rb-tree...
building hash-table...
linear list:
1.74 seconds elapsed
0 seconds in GC
0 mutations
161 minor GCs
0 major GCs
hash-table:
0.03 seconds elapsed
0 seconds in GC
0 mutations
779 minor GCs
0 major GCs
rb:
0.27 seconds elapsed
0.02 seconds in GC
0 mutations
4684 minor GCs
0 major GCs
26 just for the heck of it