eflisp r7rs level development notes

status

Most things now seem to work; eflisp is built with r7rs features built in.

using

rlwrap ./eflisp -- runs the fully built r7rs system (or whatever boot image is integrated with the application)

rlwrap ./eflisp -b -- runs the base eflisp with flisp.boot

rlwrap ./eflisp -s -- runs the base eflisp with efscm.boot yielding r7rs at the prompt

developing

From a fresh start of eflisp base:

rlwrap ./eflisp -b

(load "r7expander.lsp")

to debug inside "r7expander.lsp"

Use

make efscm.boot

rlwrap ./eflisp -s

(load "tests/chibi/r7rs-tests.scm") to look for breakage

or

make efscm.boot

make flisp.boot.inc

make

to build the fully built r7rs system; again testing for breakage with

rlwrap ./eflisp

(load "tests/chibi/r7rs-tests.scm")

or use make r7rs-coverage-test for another test of a fully built r7rs eflisp

VS Code detail

Cmd + K M can be used to set a language file extension association. I did this for .sld and .lsp to scheme.

TODO

  • there is a spooky bug in the OG chibi definition of quasiquote in r7expander.lsp; using named let instead of define fixed the problem, but sheesh! What's up? (This look(s/d) an eflisp base level bug) but now, it's BACK intermittently with the named let version of quasiquote. The test case fron r7rs-tests.scm is \(a `(b ,(+ 1 2) ,(foo ,(+ 1 3) d) e) f)`

  • The delete-duplicates in system.lsp avoids n**2 perfomance by using an eq-hashtable... it would be nice to take advantage of this somehow for the more general version in (scheme list) Also note the nice length= and length>

  • Libraries are loaded only once no matter how many includes reference them.1 It might be nice to have a means to unload a library for debugging; other library debugging tools would also be welcome.

  • perhaps integrate with base file loader's resolve-file-path: include load

  • may need to define exit in process-context library as (raise 'exit)

  • remove redundancies

  • self host? i.e., build compiler + r7expander with r7expander; this will require rethinking how make-system-image choses bindings to dump; perhaps a new version of it should be passed a list of library specs to dump instead of dumping all bound symbols

perhaps not TODO

  • printing synclosed symbols sucks... the whole environment is printed; adding unwrap-syntax in places helps; this sometimes blows the stack and dumps core; an option is to add record specific print functions when not printing values than need *print-readably*

  • write in r7rs: "Datum labels must not be used if there are no cycles." Oops.

  • maybe load should look at 1st and only form to see if it's define-library

  • (lambda () (begin)) is legal syntax but is rejected in r7expander's expand-body

  • eflisp string escape x was not compatible with r7rs ("e[1He[J" isn't "x1B;[1Hx1B;[J"); r7rs requires a terminating semicolon, which is discarded; eflisp used to terminate on any non-hex character, and ungetc it. The reader is now fixed to handle escape x (a new escape X replaces the original, needed for boot image dumps), but the print function still uses e for x1B; (which is normally fine, but might cause some pedantic tests to fail).

  • investigate whether it's necessary to incorporate eflisp compiler's support for keyword args (I don't think they're used)

  • It (was? I think this is now fixed) particularly annoying that there is no error checking on library import/export; you'll see errors about car or cdr getting a non-pair from the assq in the library.

R7RS compliance

  • Most libraries identified in r7rs are implemented. Currently missing: (scheme r5rs) (scheme complex)

  • No reentry of continuations, so call/cc is kludged

  • guard and with-exception-handler pass all the SRFI-34 examples, and all but one of the chibi tests; for some reason re-raising the exeption if with-exception-handler's handlers don't escape breaks things

  • delete-file is not implemented

  • No support for rationals, bignums, complex, inexact & exact -- this results in many failures in the reader

  • integer? in femtolisp is used for identifying ctypes that are not floating point... I'm wary of changing that; r7rs says it also should return true for (= x (round x)) ... hmm; this also affects rational?

  • No string modification, no: string-set! string-copy! string-fill! -- it's possible these could be implemented by checking that the byte size of the "hole" in the destination is the same byte size as the source, and giving an error only when they are different, but there really is no pressing need

  • Foldcase is kludged with lowercase instead; generally poor Unicode support

  • Unicode support is very basic in general; several special cases fail in the Chibi tests

  • No error using a closed string port as long as the memory buffer is still ok

  • apply ignores dotted list last cdrs rather than giving an error

  • The guard syntax is incomplete... it doesn't re-raise if the handler passes up the exception

  • The femtolisp reader is not compatible in at least these ways:

    • No support for line continuation with \ in strings
    • No support for rationals, bignums, complex, inexact & exact
    • No support for 'S' 's' 'F' 'f' 'D' 'd' 'L' 'l' exponent markers to indicate size
    • Decimal point in a number translates to floating point even if all the following digits are zero
    • Floating point reader does not handle bases other then 10
    • No character escape inside ||
    • [ is accepted to introduce a vector (as is #()
    • Symbols are permitted to contain some characters r7rs restricts, e.g., + and - as initial characters
    • No support for case-insensitive reader
    • Directives #!fold-case and #!no-fold-case break the reader
    • The number #d.1 messes up the reader because it doesn't see a digit after the d
    • The #x reader was made compatible, I think, by adding #X for the old behavior
  • The femtolisp writer is not compatible in at least these ways:

    • Datum labels are used even if the structure printed is not circular
    • There are extra character escapes, so text written is sometimes different than expected
    • Due to reader incompatibilies, especially for extra characters allowed in symbols, symbols are sometimes printed in a way that cannot be read by another r7rs implementation
  • The femtolisp compiler does some inlining, so sometimes errors that might be expected at runtime occur at compile time, e.g., with apply

Notes

Grokking r7expander

  • The (r7expander native) library imports from eflisp base and exports those symbols

  • The (r7expander builtin) library defines and exports expanders, implemented on eflisp base augmented with the (r7expander library) and (r7expander syntactic-closure) capabilities; it employs the low level macro mechanisms, such as er-macro-transformer, but doesn't export them!?

  • The (scheme base) library imports from (r7expander builtin) and (r7expander native) and exports the usual suspects. It defines all the required syntax using the syntax-rules imported from r7expander builtin).

  • The philosophy for loading files seems to be:

    • for library loading use (import <library-specifier>)
    • for list of forms to exaluate in the current environment use load
  • r7expander uses (if #f #f) to get an unspecified value; this is leading to some incompatibilities with eflisp, which expects some forms, like cond, to return #f if no cases match rather than the "unspecified value," which unfortunately is now #t (the result of (if #f #f) in efisp).

  • library-export adds one entry to the object-exports of the current library, either a symbol mapping to itself or a nickname-id pair if called with (rename id nickname) -- note that it does not check that the id is present in the library's environment.

  • library-import creates a map of renames and then installs these bindings into the top level environment. When it processes exports from libraries in it's argument list, it assumes the names in the export list of the library are bound in the library's env (this is now checked!).

Porting notes

  • These eflisp macros are obsolete now r7expander is complete and should not be translated:

    • define-macro
    • import
    • define-record-field define-record-modifier define-record-constructor define-record-accessor define-record-predicate
  • These eflisp macros are used locally and would only need to be translated to syntax-rules if/when we try to self-host:

    • check-arg is local to srfi-1
    • r-from-q-n1-n2 is local to the lib/scheme veneer
    • case-pattern is local to r7expander.lsp and already has a built-in replacement
  • dotimes is implemented in (eflisp debug) for use in that file and exported

history of porting r7expander to eflisp

  1. Took the original nyuichi files and combined into one .lsp file that can bootstrap eflisp base to have define-library and syntax-rules. The original define-library wrappers of (r7expander syntactic-closure) and (r7expander library) are commented out since our base environment doesn't have define-library. These two modules are inlined into r7expander.lsp and followed by the relevant pieces of r7expander's init.scm and main.scm.

  2. Needed to rename environment record type because it conflicts witht the builtin function of the same name used by eflisp base to identify symbols for make-system-image and also used in print-stack-trace in the toplevel exception handler. For consistency I used lozenge like enclosures, e.g., <environment>, for all record types

  3. Needed to rename r7expander's expand in order to keep the base level expand in place for debugging alongside the r7expander version, so it's now called r7expand.

  4. There was one use of syntax-rules in (r7expander builtin) that I had to replace with a macro; I used (bound? '*syntax-environment*) as a way to select between the macro and rules versions of case-pattern.

  5. Replaced the one use of case-lambda in r7expander with a dotted arg list

  6. Integrate eflisp compiler special forms in place of rexpander define-syntax versions in init/base.sld: and and or

  7. Implement (eflisp native) (built into r7expander.scm) to get access to compiler and runtime builtins

  8. Implement eflisp specific define-record-type expander in (r7expander builtin)

  9. Begin implementation of (eflisp exceptions) library with unwind-protect catch and throw after including trycatch and prog1 in (eflisp native)

  10. Implement eflisp specific parameterize expander in (r7expander builtin) using (eflisp exceptions)

  11. Extend (eflisp exceptions) library with assert, receive, which is really SRFI-8, time, with-bindings, with-input-from, and with-output-to

  12. Replace case macro in (scheme base) with version from r7rs because it failed this test: (assert (eq? 'c (case (car '(c d)) ((a e i o y) 'vowel) ((w j) 'semivowel) (else => (lambda (x) x))))) eval: variable scheme.base:=> has no value`

  13. Fix if-expand by exporting library from (r7expander builtin) and importing it into (scheme base) to pass this test case: (cond-expand ((or (library (scheme base))) (assert #t)) (else (assert #f)))

  14. Implement evaluating repl using: ((compile-thunk (expand-repl v repl-environment)))

  15. Implement load using same eval as repl, and implement (interaction-environment); export both from (scheme base) for now

  16. Incorporate eflisp compiler's support for optional lambda args in (r7expander builtin))

  17. Remove unwrap-syntax from (r7expander builtin)'s quote to avoid crashing on circular structure

  18. Use call-with-current-continuation instead of call/cc in base.sld for guard because call/cc isn't defined at this point

  19. Define the function features to export from (r7expander native)

  20. Add support for eflisp compiler special forms: for while

  21. Add auto-load of library files in library-import

  22. Add a command line option to runtime to boot into r7rs mode, -s in parallel with -b for base mode

  23. Integrate with make-system-image & jetison *syntax-environment* and base eval/repl and add a new bootstrapping script for rsr7 level above base

  24. Replaced quasiquote with chibi scheme version

  25. Added lambda-case using syntax-rules

  26. Replacedsyntax-rules with chibi scheme's version (was missing (... <template>) for quoting elipses)

  27. Implement syntax-quote, which essential to make syntax-rules work (it does now!)

    • Fix for defines nested in begin at top level of macro expansion; two pass expansion added
  28. Use fix! in new er-macro-transformer based letrec and letrec* for eflisp compiler optimization

  29. New (r7expander syntactic-closure) library now supports low level macros e.g., er-macro-transformer in user code

  30. Fun fact: FL_UNSPECIFIED == FL_T so the implemented fix for read from string returning # when reading the last value in a stirng fails for the case "#t" -- there's gotta be a better way to detect EOF! Update: perhaps this is fixed. Now fl_read_sexpr returns FL_EOF when it cannot read anything for any reason (in place of FL_UNSPECIFIED) and fl_read checks for EOF before calling fl_read_sexpr

  31. Add an (eflisp debug) library for disassembly, trace, and timing execution

  32. Add SRFI-8 receive

  33. Fix bugs in define-record-type by replacing version in r7expander with er-macro-transformer in base.sld

r7rs-coverage results

cd tests/r7rs-coverage && ./coverage eflisp | grep ERROR
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,complex?,complex literal 3+1i
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,denominator,
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,inexact,
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,numerator,
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,rational?,
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,rationalize,
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,string-copy!,
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,string-copy!,start
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,string-copy!,start and end
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,string-fill!,
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,string-fill!,start
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,string-fill!,start and end
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,string-set!,
eflisp,2025-06-23T17:53:13,(scheme base),ERROR,literals,rational
eflisp,2025-06-23T17:53:13,(scheme complex),ERROR,angle,
eflisp,2025-06-23T17:53:13,(scheme complex),ERROR,imag-part,
eflisp,2025-06-23T17:53:13,(scheme complex),ERROR,magnitude,
eflisp,2025-06-23T17:53:13,(scheme complex),ERROR,make-polar,
eflisp,2025-06-23T17:53:13,(scheme complex),ERROR,make-rectangular,
eflisp,2025-06-23T17:53:13,(scheme complex),ERROR,real-part,

r7rs-benchmarks results

Of 57 tests, 50 pass, 2 fail for no (scheme complex), 2 fail for no bignums, 3 fail for no string-set!

Benchmarking eflisp on Thu Jun 26 09:49:37 EDT 2025 
under Darwin mini-e.local 24.5.0 Darwin Kernel Version 24.5.0: 
Tue Apr 22 19:54:43 PDT 2025; root:xnu-11417.121.6~2/RELEASE_ARM64_T8132 arm64

Running browse:2000
Elapsed time: 2.295 seconds (2.295) for browse:2000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,browse:2000,2.295

Running deriv:10000000
Elapsed time: 6.097 seconds (6.097) for deriv:10000000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,deriv:10000000,6.097

Running destruc:600:50:4000
Elapsed time: 14.777 seconds (14.777) for destruc:600:50:4000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,destruc:600:50:4000,14.777

Running diviter:1000:1000000
Elapsed time: 10.281 seconds (10.282) for diviter:1000:1000000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,diviter:1000:1000000,10.281

Running divrec:1000:1000000
Elapsed time: 11.851 seconds (11.851) for divrec:1000:1000000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,divrec:1000:1000000,11.851

Running puzzle:1000
Elapsed time: 35.243 seconds (35.243) for puzzle:1000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,puzzle:1000,35.243

Running triangl:22:1:50
Elapsed time: 18.451 seconds (18.451) for triangl:22:1:50
+!CSVLINE!+eflisp-2025-06-23T17:53:13,triangl:22:1:50,18.451

Running tak:40:20:11:1
Elapsed time: 14.244 seconds (14.244) for tak:40:20:11:1
+!CSVLINE!+eflisp-2025-06-23T17:53:13,tak:40:20:11:1,14.244

Running takl:40:20:12:1
Elapsed time: 33.368 seconds (33.368) for takl:40:20:12:1
+!CSVLINE!+eflisp-2025-06-23T17:53:13,takl:40:20:12:1,33.368

Running ntakl:40:20:12:1
Elapsed time: 33.476 seconds (33.476) for ntakl:40:20:12:1
+!CSVLINE!+eflisp-2025-06-23T17:53:13,ntakl:40:20:12:1,33.476

Running cpstak:40:20:11:1
Elapsed time: 30.958 seconds (30.957) for cpstak:40:20:11:1
+!CSVLINE!+eflisp-2025-06-23T17:53:13,cpstak:40:20:11:1,30.958

Running ctak:32:16:8:1
Elapsed time: 24.498 seconds (24.498) for ctak:32:16:8:1
+!CSVLINE!+eflisp-2025-06-23T17:53:13,ctak:32:16:8:1,24.498

Running fib:40:5
Elapsed time: 33.129 seconds (33.129) for fib:40:5
+!CSVLINE!+eflisp-2025-06-23T17:53:13,fib:40:5,33.129

Running fibc:30:10
Elapsed time: 19.712 seconds (19.713) for fibc:30:10
+!CSVLINE!+eflisp-2025-06-23T17:53:13,fibc:30:10,19.712

Running fibfp:35:10
Elapsed time: 9.176 seconds (9.176) for fibfp:35:10
+!CSVLINE!+eflisp-2025-06-23T17:53:13,fibfp:35:10,9.176

Running sum:10000:200000
Elapsed time: 20.194 seconds (20.194) for sum:10000:200000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,sum:10000:200000,20.194

Running sumfp:1000000:500
Elapsed time: 14.781 seconds (14.78) for sumfp:1000000:500
+!CSVLINE!+eflisp-2025-06-23T17:53:13,sumfp:1000000:500,14.781

Running fft:65536:100
Elapsed time: 6.353 seconds (6.352) for fft:65536:100
+!CSVLINE!+eflisp-2025-06-23T17:53:13,fft:65536:100,6.353

Running mbrot:75:1000
Elapsed time: 17.421 seconds (17.42) for mbrot:75:1000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,mbrot:75:1000,17.421

Testing mbrotZ under eflisp
error: library not found: (scheme complex)
+!CSVLINE!+eflisp,mbrotZ,CRASHED

Running nucleic:50
Elapsed time: 9.824999999999999 seconds (9.824999999999999) for nucleic:50
+!CSVLINE!+eflisp-2025-06-23T17:53:13,nucleic:50,9.824999999999999

Testing pi under eflisp
parse-error: read: overflow in numeric constant 314159265358979323846264338327950288419716939937507
+!CSVLINE!+eflisp,pi,CRASHED

Running pnpoly:1000000
Elapsed time: 41.274 seconds (41.274) for pnpoly:1000000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,pnpoly:1000000,41.274

Running ray:50
Elapsed time: 11.259 seconds (11.259) for ray:50
+!CSVLINE!+eflisp-2025-06-23T17:53:13,ray:50,11.259

Running simplex:1000000
Elapsed time: 23.016 seconds (23.015) for simplex:1000000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,simplex:1000000,23.016

Running ack:3:12:2
Elapsed time: 26.594 seconds (26.594) for ack:3:12:2
+!CSVLINE!+eflisp-2025-06-23T17:53:13,ack:3:12:2,26.594

Running array1:1000000:500
Elapsed time: 27.641 seconds (27.641) for array1:1000000:500
+!CSVLINE!+eflisp-2025-06-23T17:53:13,array1:1000000:500,27.641

Running string:500000:100
Elapsed time: 1.538 seconds (1.537) for string:500000:100
+!CSVLINE!+eflisp-2025-06-23T17:53:13,string:500000:100,1.538

Running sum1:25
Elapsed time: 0.28 seconds (0.28) for sum1:25
+!CSVLINE!+eflisp-2025-06-23T17:53:13,sum1:25,0.28

Running cat:50
Elapsed time: 11.341 seconds (11.341) for cat:50
+!CSVLINE!+eflisp-2025-06-23T17:53:13,cat:50,11.341

Running tail:50
Elapsed time: 10.249 seconds (10.249) for tail:50
+!CSVLINE!+eflisp-2025-06-23T17:53:13,tail:50,10.249

Running wc:inputs/bib:50
Elapsed time: 13.405 seconds (13.405) for wc:inputs/bib:50
+!CSVLINE!+eflisp-2025-06-23T17:53:13,wc:inputs/bib:50,13.405

Running read1:2500
Elapsed time: 0.585 seconds (0.584) for read1:2500
+!CSVLINE!+eflisp-2025-06-23T17:53:13,read1:2500,0.585

Testing compiler under eflisp
error: library not found: (scheme complex)
+!CSVLINE!+eflisp,compiler,CRASHED

Running conform:500
Elapsed time: 23.779 seconds (23.779) for conform:500
+!CSVLINE!+eflisp-2025-06-23T17:53:13,conform:500,23.779

Running dynamic:500
Elapsed time: 11.552 seconds (11.552) for dynamic:500
+!CSVLINE!+eflisp-2025-06-23T17:53:13,dynamic:500,11.552

Running earley:1
Elapsed time: 12.403 seconds (12.403) for earley:1
+!CSVLINE!+eflisp-2025-06-23T17:53:13,earley:1,12.403

Running graphs:7:3
Elapsed time: 39.105 seconds (39.105) for graphs:7:3
+!CSVLINE!+eflisp-2025-06-23T17:53:13,graphs:7:3,39.105

Running lattice:44:10
Elapsed time: 77.818 seconds (77.818) for lattice:44:10
+!CSVLINE!+eflisp-2025-06-23T17:53:13,lattice:44:10,77.818

Running matrix:5:5:2500
Elapsed time: 17.573 seconds (17.573) for matrix:5:5:2500
+!CSVLINE!+eflisp-2025-06-23T17:53:13,matrix:5:5:2500,17.573

Running maze:20:7:10000
Elapsed time: 14.106 seconds (14.105) for maze:20:7:10000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,maze:20:7:10000,14.106

Running mazefun:11:11:10000
Elapsed time: 15.09 seconds (15.09) for mazefun:11:11:10000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,mazefun:11:11:10000,15.09

Running nqueens:13:10
Elapsed time: 31.633 seconds (31.634) for nqueens:13:10
+!CSVLINE!+eflisp-2025-06-23T17:53:13,nqueens:13:10,31.633

Running paraffins:23:10
Elapsed time: 2.021 seconds (2.022) for paraffins:23:10
+!CSVLINE!+eflisp-2025-06-23T17:53:13,paraffins:23:10,2.021

Testing parsing under eflisp
eval: variable string-set! has no value
+!CSVLINE!+eflisp,parsing,CRASHED

Running peval:2000
Elapsed time: 13.993 seconds (13.993) for peval:2000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,peval:2000,13.993

Running primes:1000:10000
Elapsed time: 6.591 seconds (6.592) for primes:1000:10000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,primes:1000:10000,6.591

Running quicksort:10000:2500
Elapsed time: 28.369 seconds (28.369) for quicksort:10000:2500
+!CSVLINE!+eflisp-2025-06-23T17:53:13,quicksort:10000:2500,28.369

Testing scheme under eflisp
eval: variable string-set! has no value
+!CSVLINE!+eflisp,scheme,CRASHED

Testing slatex under eflisp
eval: variable string-set! has no value
+!CSVLINE!+eflisp,slatex,CRASHED

Testing chudnovsky under eflisp
parse-error: read: overflow in numeric constant 314159265358979323846264338327950288419716939937510
+!CSVLINE!+eflisp,chudnovsky,CRASHED

Running nboyer:5:1
Elapsed time: 6.865 seconds (6.865) for nboyer:5:1
+!CSVLINE!+eflisp-2025-06-23T17:53:13,nboyer:5:1,6.865

Running sboyer:5:1
Elapsed time: 8.895 seconds (8.895) for sboyer:5:1
+!CSVLINE!+eflisp-2025-06-23T17:53:13,sboyer:5:1,8.895

Running gcbench:20:1
Elapsed time: 12.09 seconds (12.09) for gcbench:20:1
+!CSVLINE!+eflisp-2025-06-23T17:53:13,gcbench:20:1,12.09

Running mperm:20:10:2:1
Elapsed time: 25.619 seconds (25.619) for mperm:20:10:2:1
+!CSVLINE!+eflisp-2025-06-23T17:53:13,mperm:20:10:2:1,25.619

Running equal:100:100:8:1000:2000:5000
Elapsed time: 1.279 seconds (1.28) for equal:100:100:8:1000:2000:5000
+!CSVLINE!+eflisp-2025-06-23T17:53:13,equal:100:100:8:1000:2000:5000,1.279

Running bv2string:1000:1000:100
Elapsed time: 10.836 seconds (10.836) for bv2string:1000:1000:100
+!CSVLINE!+eflisp-2025-06-23T17:53:13,bv2string:1000:1000:100,10.836

  1. ^ Regardless of the number of times that a library is loaded, each program or library that imports bindings from a library must do so from a single loading of that library, regardless of the number of import declarations in which it appears. That is, (import (only (foo) a)) followed by (import (only (foo) b)) has the same effect as (import (only (foo) a b)).