Here is a brief intro to a repository (and index) of specific Scheme snippets organized around a simple plan described below.

I have used this technology only in the context of a read-eval-print-loop (REPL).

I am unhappy with the implemented and proposed schemes for Scheme modules which are conceived to package Scheme functionality for use by other functions. Other module schemes of which I am aware add to the language. This module scheme uses the language as is. We agree that including all the code all the time is a bad idea. We do not agree on much else.

Retrospective Principles

It is good to have a list of desiderata before you begin design. I didn’t. In retrospect, however, here are some:

Built in to the program fileVal is the name of a directory which must probably be modified by the user to locate a directory containing the files with the Scheme expressions whose values are to be importable. The first Scheme expression in such a file is evaluated in a slightly extended R5RS environment to produce the value of the invoking (fileVal "Module") expression.

I rely here only on standard Scheme 5 library functions to provide a minimal module system and a specific Scheme repository. I have tested this pretty well with MzScheme 372. The notion is that a module is merely a file in this repository. That file contains one Scheme expression. The Scheme expression (fileVal fn) returns the value of the sole expression in the file fn in this here repository. (Actually the first expression in the file.) The expression in the file is evaluated in the standard Scheme 5 environment extended by identifiers ylppa and fileVal bound to the same function as already described. Evaluating (fileVal fn) twice with the same file name skips re-reading the file the 2nd time and returns the value produced the first time. Said otherwise (eq? (fileVal string) (fileVal string)) yields true whereas (eq? (cons 0 0) (cons 0 0)) yields false. The first expression yields true even if the contents of the file is (cons 0 0). fileVal is defined here. See this about using on your computer. The file names in the repository serve as a module name space. The defining occurrences of module names are the file names in the directory of the repository and the applied occurrences are quoted strings as arguments to fileVal.

I couldn’t think of anything simpler and this seems good enough. I am happy with this style, after adapting about 80 mostly small programs to the repository.
This plan has much in common with E-makers.

Security

Since (set! car cdr) is likely to corrupt just about all programs running in the same environment, it is perhaps pointless to worry about cross module corruption. (Later Scheme 6 versions of MzScheme reply
stdin::6: set!: cannot mutate module-required identifier in: car
to (set! car 4).) Yet there are the following issues that should be understood.

If the contents of file Cons is (cons 0 0) then the execution of (set-car! (fileVal "Cons") 2) will change the meaning of a subsequent (car (fileVal "Cons")) in any environment. The current content of the repository produces no such mutable values.

On the other hand if code in file zot executes (set! car cdr) then (car '(2 . 3)) => yields 2 even after performing (fileVal "zot") thanks to the fact that the file code runs in a different environ. As the repository stands now however executing (set-car! (fileVal "Matrix")) ruins the subsequent meaning of (car (fileVal "Matrix")) in all other environments.

I think it would be easy to write a shallow mutable copier which would return a new mutable value to each invocation of fileVal and thus shut off such interference between users. I know no automatic test that repository members do not return one mutable value to different callers. A run-time enforcement would be expensive.

The semantics of fileVal can be approximately defined by replacing each occurrence of (fileVal name) by the first Scheme expression in the repository file named name. A difference is that this would allow free variables within repository members to be resolved in the top level name space, a construction that is not possible with fileVal.

Other Languages

Portions of Scheme programs work with other parts by producing Scheme values for those other parts. In a strongly typed language some parts describe type constraints on other parts. It is conventional to hide the constraining parts in header files such as in common C practice. I have no such burden here, nor does Scheme provide the performance advantages that types afford to C programs and even some type safe languages. I do not understand OCaml types, despite having written several useful OCaml programs.

I think that lambda abstraction is so powerful that macros are superfluous. I might be convinced by reading some programs that claim to be improved by macros.

In the context of JavaScript, at least, it is interesting to imagine secure hashes of sources as module names. Perhaps the browser conventionally maintains a cache of JS values indexed by the SHA of their sources, and consults the home site of the referencing JavaScript code upon cache miss. This plan might require that such JS values be transitively immutable. I am not sure whether this can be enforced mechanically.

Evidently the language E has such a scheme.

Taming

If I run your Scheme program on my computer I must trust your code not to delete all my files. Some think that this is the way the world must be and thus beneath mention. I think not. Several efforts at taming languages address this problem and I see no fault in their high level design, but I have not studied them enough to know if there remain vulnerabilities. The plan seems very plausible.

The above repository scheme does not address issues of authority outside the language platform proper but it serves as a good place to start. Deleting all my files is an example of what I will call exo-authority here. A module cannot access the dangerous Scheme primitive call-with-output-file, which conveys a great deal of exo-authority, without explicitly including that symbol. That is unless the platform has included the eval variation of one argument, left over from earlier Schemes.

(eval (list (string->symbol (string-append "call-with" "-output-file"))
    "target-file-name" '(lambda (p) (write "crud" p))))
Clobbers a file in my directory without including the name, call-with-output-file, of the Scheme primitive with the dangerous exo-authority. R5RS does not include this variation on eval but many R5RS platforms include it. The program fileVal could be modified to bind eval to a tamed function that accepted only two arguments. scheme-report-environment as provided in MzScheme must also be tamed as it carries the same dangerous weapons. This illustrates a strength in the Scheme semantics and a weakness in choice of standard library functions.

Spin Offs

Simplicity is its own reward but here are others in this particular case—rewards that I did not foresee: It is possible to ameliorate small discrepancies between implementations of Scheme by fiddling with the name space that the guest code runs in. In the 9 lines I already make the short spelling of call/cc available to all guests despite the fact that a few implementations ignore the wide-spread practice of providing the short name. It is also possible to remove or redefine capabilities to the file system and thus may the invoker guard the integrity of his underlying platform. There are a large variety of such changes that are easy for the Scheme programmer who has a good understanding of the role a Scheme environment.

vs. R7RS

The latest Scheme has an include function where
(include "header.scm")
in your source is replaced by
(begin file content)
This gives the author of the file access to your mutable environment.
(begin (define car cdr))
as file content will ruin most programs.