I'll answer the easier one first. The different behaviour is because the
define-libraryis wrapped by
begin. Larceny is using SRFI-78 (a.k.a van Tonder expander) and this expander creates a library at runtime. Thus, when the
cond-expandis expanded, the library
(dummy)is not created yet. Sagittarius, on the other hand, it creates a library at compile time. Thus, whenever
libraryis found on the code, then the library is created at that time. So
cond-expandcan find the
(dummy)during expansion. I guess, if you want to make the code work as expected, then removing
beginshould be enough.
NOTE: On R7RS (or even R6RS), the library definition can not be anywhere in program so the example code itself is not portable. For example, Chibi would raise an error if you write this on its REPL (which I think pretty much inconvenient).
beginworks only on REPL because van Tonder's expander reads all expression first then expands it. Thus, at compile time there is no library created yet.
It is basically impossible to do the ML like modular programming on R7RS Scheme, if I understood the question correctly. But there is a technique that emulate it. The key is
eval. It is easy to see on the code so code first.
;; without SRFI-39 (define-library (peano) (import (scheme base) (scheme eval)) (export one add load-peano make-peano-impl) (begin (define-record-type peano-impl (make-peano-impl one add) peano-impl? (one peano-impl-one) (add peano-impl-add)) (define global-impl #f) (define (load-peano env) (let ((impl (eval '(load-peano-impl) env))) (set! global-impl impl))) (define (one) (peano-impl-one global-impl)) (define (add a b) ((peano-impl-add global-impl) a b))) ) ;; peano-numeral.sld (define-library (peano-numeral) (import (scheme base) (peano)) (export one add load-peano-impl) (begin (define (load-peano-impl) (make-peano-impl 1 +)))) ;; peano-symbol.sld (define-library (peano-symbol) (import (scheme base) (peano)) (export one add load-peano-impl) (begin (define (load-peano-impl) (make-peano-impl '(s z) (lambda (x y) (if (eq? 'z x) y `(s ,(add (cadr x) y)))))))) (define-library (four) (import (scheme base) (peano)) (export four) (begin (define (four) (let ((two (add (one) (one)))) (add two two))))) (import (scheme write) (scheme eval) (four) (peano)) (load-peano (environment '(peano-numeral))) (four) ;; -> 4 (load-peano (environment '(peano-symbol))) (four) ;; -> (s (s (s (s z))))
evalcan take an environment so we can use that. Specifying which implementation should be used via
load-peanoby passing library name, and the
(peano)sets the actual implementation to the global context. If the implementation supports SRFI-39, then using parameter would make it thread safe.
NOTE: If the implementation supports CL like method dispatch, then this can be much simpler. (Well, using TinyCLOS provides it and it wouldn't be so difficult to make some syntax sugar for that and provide it with R7RS library system. I just don't have motivation since Sagittarius already has it...)
NOTE: Parameter in R7RS doesn't specify when parameter object (or procedure) received one argument. As far as I know, all R7RS implementations behave the same as SRFI-39 but there might be an implementation that doesn't accept it in future.
NOTE: If the implementation supports
identifier-syntaxwhich is added on R6RS, then
onecan be defined like this:
(define-syntax one (identifier-syntax ((peano-impl-one global-impl))))Then
fourcan be like this:
(define-syntax four (identifier-syntax ((lambda () (let ((two (add one one))) (add two two))))))So you don't have to write