[plt-scheme] side effects in R6RS modules

From: Michele Simionato (michele.simionato at gmail.com)
Date: Sun May 3 04:13:13 EDT 2009

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:

(library (experimental registry)
(export registry register)
(import (rnrs))

(define _registry '())

(define (registry)

(define (register id)
 (display "registering ")
 (display id)
 (set! _registry (append _registry (list id)))

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:

(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)
          ;; 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:

(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
(import (rnrs) (experimental define+) (experimental defines))
(define-registered names)
(display names)

What's the output of this script in different implementations?


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 behaves like Ypsilon. The identifiers are registered at the first

$ ikarus --compile-dependencies x.ss
registering #<syntax a [char 94 of
registering #<syntax b [char 108 of
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


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
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

Posted on the users mailing list.