[plt-scheme] side effects in R6RS modules
Warning 1: message sent to the PLT list, the Larceny list and the Ikarus list
Warning 2: this is long and complicated, for freaks of the module system only
I have been experimenting with side effects in modules.
>From my previous experience I expected to open a can of
worms, finding strange behaviours and inconsistencies
between implementations. My expectations were met ;-)
I am asking for comments from the Ones Who Knows, since
I do not understand the behaviour in Larceny and PLT.
The problem I want to solve is to introduce a define+ form which
works as a regular define, but also register the
defined name via a side effect *at expand time*.
The problem is directly inspired to the "Composable
and Compilable macros" paper and I am solving it
with the sole purpose of finding the portability
issues between different implementations.
To solve the problem, I have defined three modules,
which I put in a package named "experimental".
The first module is just a registry of identifiers:
#!r6rs
(library (experimental registry)
(export registry register)
(import (rnrs))
(define _registry '())
(define (registry)
_registry)
(define (register id)
(display "registering ")
(display id)
(newline)
(set! _registry (append _registry (list id)))
_registry)
)
The second module exports the define+ form, as well
as a define-registered form to save the list of
registered names into a runtime variable:
#!r6rs
(library (experimental define+)
(export define-registered define+)
(import (rnrs) (for (experimental registry) expand))
;; save the content of the expand-time registry into a runtime definition
(define-syntax define-registered
(lambda (x)
(syntax-case x ()
((define-registered names)
#`(define names '#,(registry))))))
(define-syntax define+
(lambda (x)
(syntax-case x ()
((define+ name value)
#'(begin
;; dirty trick to get an expand time side effect
(define-syntax dummy (begin (register #'name) (lambda (x) #f)))
;; the real definition
(define name value))))))
)
The third module contains just a couple of definitions:
#!r6rs
(library (experimental defines)
(export a b)
(import (experimental define+))
(define+ a 1)
(define+ b 2)
)
I have also defined a script importing the definitions:
$ cat x.ss
#!r6rs
(import (rnrs) (experimental define+) (experimental defines))
(define-registered names)
(display names)
What's the output of this script in different implementations?
Ypsilon
--------------------
At the first run Ypsilon register the definitions and prints them:
$ ypsilon x.ss
registering #<syntax a>
registering #<syntax b>
(a b)
At the second run the libraries are already compiled and I get the
empty list:
$ ypsilon x.ss
()
This is due to separate compilation and I understand what
it is going on.
Ikarus
----------------------------------------
Ikarus behaves like Ypsilon. The identifiers are registered at the first
compilation
$ ikarus --compile-dependencies x.ss
registering #<syntax a [char 94 of
/home/micheles/gcode/scheme/experimental/defines.sls]>
registering #<syntax b [char 108 of
/home/micheles/gcode/scheme/experimental/defines.sls]>
Serializing "/home/micheles/gcode/scheme/experimental/defines.sls.ikarus-fasl"
...
Serializing "/home/micheles/gcode/scheme/experimental/define+.sls.ikarus-fasl"
...
At subsequence runs I get the empty list:
$ ikarus --r6rs-script x.ss
()
Larceny
-----------------------------------------------------
The behavior of Larceny surprises me:
$ larceny-r6rs x.ss
registering #<record identifier>
registering #<record identifier>
registering #<record identifier>
registering #<record identifier>
(a b a b)
Why are the identifiers registered twice??
PLT Scheme
----------------------------------------------
PLT Scheme (I am using version 4.1.5) is even more surprising:
$ plt-r6rs x.ss
/home/micheles/.plt-scheme/4.1.5/collects/experimental/defines.sls:5:0:
compile: bad syntax; function application is not allowed, because no
#%app syntax transformer is bound in the transformer environment in:
(register (syntax a))
This message usually means that there is a phase error,
however I thought I did my homework correctly by using
the (for (experimental registry) expand) import form:
Larceny does not complain either, so there must be
something specific to PLT happening here.
I expected, had script compiled, to get consistently
the empty list, with or without separate compilation,
due to the multiple instantiation semantics of PLT.
Can you explain what it happening?
Michele Simionato