Hi all, I was digging into #1131 again today. So far, I haven't had much luck trying to untangle the way modules and macros interact, but I did manage to find a possible way to simplify the way primitives are handled by the module system!
First, here's a little background on how things currently work as I understand it: When a "primitive" module (like chicken, scheme, extras, etc) is registered, it marks all its identifiers as being primitive by creating an alias which is prefixed with "#%". This alias doesn't really *do* anything, except serve as a sort of anchor to ensure that the name won't be looked up in the current module. Instead, whenever a primitive alias is encountered, lookup is short-cut and the alias is replaced by the core primitive. This hack is required because the primitives are not defined inside proper modules (which would cause them to be defined as, "scheme#foo") but as globals. This is wired into the module system by pseudo-modules (the aforementioned "primitive modules"). For example, the "scheme" pseudo-module defines +. When the module is registered, it will mark the symbol #%+ as ##core#primitive with value +. The module's export list contains "+", which maps to "#%+". Then, when a module which uses the scheme module is being defined, all references to + are rewritten to #%+. When the module is resolved, all #%+ are rewritten back to +. So, in the compiled output where modules don't exist anymore, you only see references to the global variable "+", which usually holds the procedure implemented by the C_plus function. Contrast this with the "numbers" egg. This module defines +, which is automatically rewritten to numbers#+ because numbers is a proper module. Its export list maps "+" to "numbers#+". When another module is defined which imports numbers, all its references to + are rewritten to numbers#+. This _stays_ like that when the module is resolved and in the compiled output: the numbers implementation library will define the global variable "numbers#+" which holds the bignum-aware plus procedure. Now, when a module like r7rs re-exports a primitive procedure like, say, "error", its generated import library will map error to #%error. Then, ##sys#register-compiled-module will detect that this is an aliased reference to the primitive "error", and cause the module's meta expressions to register the alias like that (so that when this module is imported by another, it will know about these aliases). However, the r7rs module's import library itself will already load the primitive "scheme" module as well, which will already take care of registering the alias. So I think the primitive detection code can be removed. This is what the attached patch does; it is a worthwhile simplification of ##sys#register-compiled-module, I think. I'm just not 100% sure whether this analysis is correct because this module stuff still confuses me. Any feedback would be appreciated! This differentiation between primitive and non-primitive values is a little unfortunate, and I eventually hope to get rid of that, but that's trickier than you might expect: we want to continue supporting R5RS code and any program which doesn't make use of modules at all. And there, you can do things like (set! + "foo") to overwrite the implementation. The simplest way I can think of to do this with the current code base is to change library.scm to actually _define_ a global variable called #%+, and to make that the primitive. Any reference to + in toplevel code gets rewritten to #%+, and the module system can do the same through the mechanism we already have. Anyway, that's for another day but I wanted to put it out there for y'all to consider. Cheers, Peter -- http://www.more-magic.net
>From d43d075da57da72522a9db088ae904569df0d795 Mon Sep 17 00:00:00 2001 From: Peter Bex <[email protected]> Date: Sun, 20 Jul 2014 15:50:02 +0200 Subject: [PATCH] Get rid of explicit marking of primitives re-exported by modules. When importing a module, its dependencies always *must* be imported, because a re-export could involve a macro which is defined by another module, and the definitions of macros are handled through the import library. Because this is happening anyway, any module re-exporting a primitive will end up importing the core module which exports it. This import will execute ##sys#register-primitive-module which causes the primitives to be marked though ##sys#primitive-alias. Because of this recursive module loading, it's unnecessary to detect primitives at module registration time. This simplifies the module registration code a little (which is already hairy enough of itself). --- expand.scm | 5 ---- modules.scm | 96 ++++++++++++++++++++++++++--------------------------------- 2 files changed, 43 insertions(+), 58 deletions(-) diff --git a/expand.scm b/expand.scm index f14c79f..8f922db 100644 --- a/expand.scm +++ b/expand.scm @@ -921,11 +921,6 @@ ;;; Macro definitions: -(define (##sys#mark-primitive prims) - (for-each - (lambda (a) (putp (cdr a) '##core#primitive (car a))) - prims)) - (##sys#extend-macro-environment 'import '() (##sys#er-transformer diff --git a/modules.scm b/modules.scm index e102b9d..a59aa5b 100644 --- a/modules.scm +++ b/modules.scm @@ -681,59 +681,49 @@ (for-each (lambda (spec) (let-values (((vsv vss vsi) (import-spec spec))) - (let ((prims '())) - (dd `(IMPORT: ,loc)) - (dd `(V: ,(if cm (module-name cm) '<toplevel>) ,(map-se vsv))) - (dd `(S: ,(if cm (module-name cm) '<toplevel>) ,(map-se vss))) - (##sys#mark-imported-symbols vsv) ; mark imports as ##core#aliased - (for-each - (lambda (imp) - (let* ((id (car imp)) - (aid (cdr imp)) - (prim (getp aid '##core#primitive))) - (when prim - (set! prims (cons imp prims))) - (and-let* ((a (assq id (import-env))) - ((not (eq? aid (cdr a))))) - (##sys#notice "re-importing already imported identifier" id)))) - vsv) - (for-each - (lambda (imp) - (and-let* ((a (assq (car imp) (macro-env))) - ((not (eq? (cdr imp) (cdr a))))) - (##sys#notice "re-importing already imported syntax" (car imp))) ) - vss) - (when reexp? - (unless cm - (##sys#syntax-error-hook loc "`reexport' only valid inside a module")) - (let ((el (module-export-list cm))) - (cond ((eq? #t el) - (set-module-sexports! cm (append vss (module-sexports cm))) - (set-module-exist-list! - cm - (append (module-exist-list cm) - (map car vsv) - (map car vss)))) - (else - (set-module-export-list! - cm - (append - (let ((xl (module-export-list cm))) - (if (eq? #t xl) '() xl)) - (map car vsv) - (map car vss)))))) - (set-module-iexports! - cm - (merge-se (module-iexports cm) vsi)) - (when (pair? prims) - (set-module-meta-expressions! - cm - (append - (module-meta-expressions cm) - `((##sys#mark-primitive ',prims))))) - (dm "export-list: " (module-export-list cm))) - (import-env (append vsv (import-env))) - (macro-env (append vss (macro-env)))))) + (dd `(IMPORT: ,loc)) + (dd `(V: ,(if cm (module-name cm) '<toplevel>) ,(map-se vsv))) + (dd `(S: ,(if cm (module-name cm) '<toplevel>) ,(map-se vss))) + (##sys#mark-imported-symbols vsv) ; mark imports as ##core#aliased + (for-each + (lambda (imp) + (and-let* ((id (car imp)) + (a (assq id (import-env))) + (aid (cdr imp)) + ((not (eq? aid (cdr a))))) + (##sys#notice "re-importing already imported identifier" id))) + vsv) + (for-each + (lambda (imp) + (and-let* ((a (assq (car imp) (macro-env))) + ((not (eq? (cdr imp) (cdr a))))) + (##sys#notice "re-importing already imported syntax" (car imp))) ) + vss) + (when reexp? + (unless cm + (##sys#syntax-error-hook loc "`reexport' only valid inside a module")) + (let ((el (module-export-list cm))) + (cond ((eq? #t el) + (set-module-sexports! cm (append vss (module-sexports cm))) + (set-module-exist-list! + cm + (append (module-exist-list cm) + (map car vsv) + (map car vss)))) + (else + (set-module-export-list! + cm + (append + (let ((xl (module-export-list cm))) + (if (eq? #t xl) '() xl)) + (map car vsv) + (map car vss)))))) + (set-module-iexports! + cm + (merge-se (module-iexports cm) vsi)) + (dm "export-list: " (module-export-list cm))) + (import-env (append vsv (import-env))) + (macro-env (append vss (macro-env))))) (cdr x)) '(##core#undefined)))) -- 1.7.10.4
_______________________________________________ Chicken-hackers mailing list [email protected] https://lists.nongnu.org/mailman/listinfo/chicken-hackers
