choice for embedding Scheme implementation?

85 views
Skip to first unread message

Todd Gillespie

unread,
May 13, 2002, 2:59:48 AM5/13/02
to

I am looking at implementing a Scheme in a network application I am
working on. What I can't conclude thus far, is which Scheme
implementation is "best" for my uses? Best, in this case, is speed of
embedding, conformance to R5RS, and most useful community&docs.

Ideally, it should play well with C (as the low-level parts of the app are
in C), should be thread-safe, and have nice libraries.

The two leaders in my mind are Guile (for ease of embedding) and MzScheme
(for most of the 2nd list). Are there any better options? Why?

TIA

felix

unread,
May 13, 2002, 3:50:24 AM5/13/02
to

Todd Gillespie wrote in message ...


An alternative would be the Chicken Scheme compiler. It allows
embedding compiled code easily into C and has a quite flexible
C interface. For more information, see:

http://www.call-with-current-continuation.org/chicken.html

Note that it isn't currently thread-safe. But Chicken has it's own
multithreading, based on first class continuations.


cheers,
felix


Todd Gillespie

unread,
May 13, 2002, 5:55:46 AM5/13/02
to
felix <felixu...@freenet.de> wrote:

: An alternative would be the Chicken Scheme compiler. It allows


: embedding compiled code easily into C and has a quite flexible
: C interface. For more information, see:

: http://www.call-with-current-continuation.org/chicken.html

: Note that it isn't currently thread-safe. But Chicken has it's own
: multithreading, based on first class continuations.

That's a bit trickier, what with having to ask Chicken for a thread
whenever an app (C) thread has some Scheme code to interpret. I've so for
only done the standard: create a stack of thread-safe interpreters, each
fully independent of the others; the handling is simpler that way.

I didn't see any help on the embedding API in the manual; is there one?

felix

unread,
May 13, 2002, 7:36:11 AM5/13/02
to

Todd Gillespie wrote in message ...

See

http://www.call-with-current-continuation.org/manual.html#entrypoints
and
http://www.call-with-current-continuation.org/manual.html#callbacks

You can also take a look at:

http://www.call-with-current-continuation.org/stubmaker

which is a simple script that generates C wrappers for Scheme
code.

If you have any more questions or need some examples, please feel free to
contact me at
felix @ call-with-current-continuation.org


cheers,
felix


Michael Sperber [Mr. Preprocessor]

unread,
May 13, 2002, 7:53:39 AM5/13/02
to
>>>>> "Todd" == Todd Gillespie <to...@math.utexas.edu> writes:

Todd> I am looking at implementing a Scheme in a network application I am
Todd> working on. What I can't conclude thus far, is which Scheme
Todd> implementation is "best" for my uses? Best, in this case, is speed of
Todd> embedding, conformance to R5RS, and most useful community&docs.

Todd> Ideally, it should play well with C (as the low-level parts of the app are
Todd> in C), should be thread-safe, and have nice libraries.

Todd> The two leaders in my mind are Guile (for ease of embedding) and MzScheme
Todd> (for most of the 2nd list). Are there any better options? Why?

Depending on what you mean by "embedding," Scheme 48/scsh might also
be a viable choice. It has an extensive C interface, has nice
libraries, whatever that means (especially pertaining to networking.)

I don't know what exactly you mean by "thread-safe." If the question
is whether several VMs can run concurrently in different OS-level
threads, the answer is no. I'm pretty sure this is also the case in
MzScheme, but don't know about Guile.

--
Cheers =8-} Mike
Friede, Völkerverständigung und überhaupt blabla

Jens Axel Søgaard

unread,
May 13, 2002, 10:14:16 AM5/13/02
to
Todd Gillespie wrote:
> I am looking at implementing a Scheme in a network application I am
> working on. What I can't conclude thus far, is which Scheme
> implementation is "best" for my uses?

For the sake of curiosoty I decided to see how to embed MzScheme.
Be aware that Google digs up the embedding instructions for the version 103.

The new are located is here:

http://download.plt-scheme.org/doc/200alpha12/html/insidemz/insidemz-Z-H-1.h
tml#%_sec_1.2

--
Jens Axel Søgaard


David Rush

unread,
May 16, 2002, 8:34:03 PM5/16/02
to

Well, plowing my way through the recent flamewar has got my dander up
a bit, so here's a useless $0.02.

Ditch R5RS-compliance, it was a step backwards. Use Gambit-C (although
R5RS comliance is rumored to be in the works for it). It's fast, fast,
fast, *and* Oleg Kiselyov uses it, *and* Marc Feeley wrote the
Multi-threading SRFI. What more could you ask for?

obGuileSlam: It's got the most annoying module system in the history of Lisp.

david rush
--
Computers save time like kudzu prevents soil erosion.
-- Michael J. Fromberger (on comp.lang.scheme)

Todd Gillespie

unread,
May 17, 2002, 3:22:39 PM5/17/02
to
David Rush <ku...@bellsouth.net> wrote:
: Well, plowing my way through the recent flamewar has got my dander up

: a bit, so here's a useless $0.02.

Which flamewar?

: Ditch R5RS-compliance, it was a step backwards. Use Gambit-C (although


: R5RS comliance is rumored to be in the works for it). It's fast, fast,
: fast, *and* Oleg Kiselyov uses it, *and* Marc Feeley wrote the
: Multi-threading SRFI. What more could you ask for?

Could you explain why R5RS is a step back from R4RS? (assuming that
there's no intermediate standard between them)?
I am also unfamiliar with Kiselyov or Feeley. Who are they?

I've already written the first version with TinyScheme; in the future I'll
be moving to something more complete.

: obGuileSlam: It's got the most annoying module system in the history of Lisp.

It's embedding features are rather less than I was hoping for from an
implementation designed for embedding. I hear their next version should
be better..

Thien-Thi Nguyen

unread,
May 17, 2002, 6:41:49 PM5/17/02
to
David Rush <ku...@bellsouth.net> writes:

> obGuileSlam: It's got the most annoying module system in the history of Lisp.

it's nice to see guile distinguish itself in some way...
ob-guile-student-query: what do find annoying about it?

thi

felix

unread,
May 20, 2002, 7:49:20 AM5/20/02
to

David Rush wrote in message ...

>>
>> Could you explain why R5RS is a step back from R4RS?
>
>Just my arrogant opinions, but:
>
> * dynamic-wind
> * call-with-values
> * eval
> * <flame-bait>the macro system</flame-bait>
>
>The main features, really...
>
>The first two have been discussed to death here over the last
>year. You should be able to google for them pretty easily. EVAL as
>specified in R5RS is *completely* useless w/out first-class
>environments.

Could you elaborate on that, please? What does "completely useless"
mean to you? Does it mean "completele useless for ME", or does it
mean completele useless in general? Why would it be more useful
with first-class environments?

What are you talking about???


cheers,
felix


David Rush

unread,
May 20, 2002, 12:29:50 PM5/20/02
to
"felix" <felixu...@freenet.de> writes:
> David Rush wrote in message ...
> >> Could you explain why R5RS is a step back from R4RS?
> >
> >Just my arrogant opinions, but:
> > * eval

> >EVAL as
> >specified in R5RS is *completely* useless w/out first-class
> >environments.
>
> Could you elaborate on that, please? What does "completely useless"
> mean to you? Does it mean "completele useless for ME", or does it
> mean completele useless in general?

It certainly means completely useless for me. I think it is fairly
useless in general. The problem is that there is no guarantee that any
modifications to the environment are still going to be there next time
you call eval. Hence:

(eval '(define (foo x) (and (equal? x 42)
"the ultimate answer!"))
(interaction-environment))
(eval '(foo 42) (interaction-environment))

*may* result in an "undefined foo" error. I would have sworn up until
5 minutes ago that I actually discovered an example of such annoying
behavior in one of the more well-known Schemes, but I can't remember
which one it is right now. R5RS certainly allows this behavior.

It is also not entirely obvious which (if any) program definition are
present in (interaction-environment). It seems to mostly be the case
that everything from the program is there, but again, I wouldn't be
kvetching about this if it hadn't tripped me up. Essentially, I
concluded that if I wanted portably predictable behavior, that I
needed to implement my own eval.

I should add that I am also concerned with the behavior of eval in
compiled programs which *don't* have the REPL running.

> Why would it be more useful with first-class environments?

Because then you could control what is in each environment. This is
really useful for sandboxing and namespace control of user-evaluated
code.

david rush
-----BEGIN GEEK CODE BLOCK-----
Version 3.12
GCS d? s-: a C++$ ULSAH+++$ P+(---) L++ E+++ W+(--) N++ K w(---) O++@
PS+++(--) PE(++) Y+ PGP !tv b+++ DI++ D+(--) e*(+++>+++) h---- r+++
z++++
-----END GEEK CODE BLOCK-----

Al Petrofsky

unread,
May 20, 2002, 2:49:37 PM5/20/02
to
David Rush <ku...@bellsouth.net> writes:
> Todd Gillespie <to...@math.utexas.edu> writes:

> > David Rush <ku...@bellsouth.net> wrote:
> >
> > : Ditch R5RS-compliance, it was a step backwards.
> >
> > Could you explain why R5RS is a step back from R4RS?
>
> Just my arrogant opinions, but:
>
> * dynamic-wind
> * call-with-values
> * eval
> * <flame-bait>the macro system</flame-bait>
>
> The first two have been discussed to death here over the last
> year. You should be able to google for them pretty easily. EVAL as

> specified in R5RS is *completely* useless w/out first-class
> environments.

R5rs only requires eval, null-environment, and
scheme-report-environment, which add up to not much utility, I agree.
But how is it a step backward from r4rs, when r4rs had no eval, and
r5rs is easy to implement and is completely compatible with more
useful extensions to eval? It even specificly blesses first-class
environments as a useful extension and is deliberately designed to
accommodate them (by making eval take two arguments rather than having
an unary eval-in-standard-environment procedure).

> The macro-system, while pretty cool, has got holes in it
> big enough top drive a truck through (as Oleg Kiselyov and Al*
> Petrofsky have done) and it really should be specified via SRFI,

I don't understand this. The r5rs macro system's specification has
some ambiguities and misfeatures, but it is extraordinarily useful
compared to having no macro system, as in r4rs. Also, it is
completely compatible with syntax-case, which I think is your
preferred macro system, so how is it a step backward?

What is your idea of what should be in SRFIs? I would think srfis
should be for these sorts of things:

-- Features that are normally implemented using more primitive
standard features.

-- Features that could be implemented using more primitive standard
features, but will commonly benefit from implementation-dependent
means.

-- Features for accessing the world outside the scheme system:
sockets, files, graphics displays, c functions, operating system
primitives, etc..

A macro system doesn't fit in there at all. On the other hand, having
a macro system in the standard has the benefit that it enables moving
things like case, do, and delay to the library.

If the standard included a simple low-level macro system with
primitives for effecting hygiene (including referential transparency
and hygienic literals matching), then it could make sense for the
pattern/template language of syntax-rules (and syntax-case) to be
specified outside of the main standard. Is that what you mean?

> Marc Feeley is one of the RnRS-editors/authors.

A look at the first page of an rnrs indicates this to be false:

Revised^5 Report on the Algorithmic Language Scheme
***************************************************

Richard Kelsey, William Clinger, and Jonathan Rees (Editors)

H. Abelson R. K. Dybvig C. T. Haynes G. J. Rozas
N. I. Adams IV D. P. Friedman E. Kohlbecker G. L. Steele Jr.
D. H. Bartley R. Halstead D. Oxley G. J. Sussman
G. Brooks C. Hanson K. M. Pitman M. Wand

-al

Lauri Alanko

unread,
May 20, 2002, 2:54:49 PM5/20/02
to
In article <87ptzqp...@radish.petrofsky.org>,

Al Petrofsky <a...@petrofsky.org> wrote:
>R5rs only requires eval, null-environment, and
>scheme-report-environment, which add up to not much utility, I agree.
>But how is it a step backward from r4rs, when r4rs had no eval, and
>r5rs is easy to implement and is completely compatible with more
>useful extensions to eval?

Easy to implement? Having eval means that you have to carry around the
compiler and a first-class environment at runtime. This is a nontrivial
requirement, IMHO, and may have a significant effect on the footprint of a
minimal lightweight implementation.


Lauri Alanko
l...@iki.fi

Sander Vesik

unread,
May 20, 2002, 4:30:16 PM5/20/02
to
David Rush <ku...@bellsouth.net> wrote:
> * <flame-bait>the macro system</flame-bait>
>
> The main features, really...

>
> The first two have been discussed to death here over the last
> year. You should be able to google for them pretty easily. EVAL as
> specified in R5RS is *completely* useless w/out first-class
> environments. The macro-system, while pretty cool, has got holes in it

> big enough top drive a truck through (as Oleg Kiselyov and Al*
> Petrofsky have done) and it really should be specified via SRFI,
> except of course that SRFIs didn't exist then...

Well, I thought what they demonstrated is the extremely usefuleness and
power of the macro system. Scheme would be considerably less useful as a
language without a macro system giving at least equvalent functionality.

<flame-bait>the macros are way more usefull than the numeric tower and
its warts anyways</flame-bait>

> david rush

--
Sander

+++ Out of cheese error +++

Al Petrofsky

unread,
May 20, 2002, 6:28:42 PM5/20/02
to
Lauri Alanko <l...@iki.fi> writes:
> Al Petrofsky <a...@petrofsky.org> wrote:

> >R5rs only requires eval, null-environment, and
> >scheme-report-environment, which add up to not much utility, I
> >agree. But how is it a step backward from r4rs, when r4rs had no
> >eval, and r5rs is easy to implement and is completely compatible
> >with more useful extensions to eval?
>
> Easy to implement? Having eval means that you have to carry around
> the compiler and a first-class environment at runtime. This is a
> nontrivial requirement, IMHO, and may have a significant effect on
> the footprint of a minimal lightweight implementation.

It only requires a simple interpreter. Below is a minimal lightweight
implementation with an insignificant footprint. It is not quite
completely r5rs-compliant, in that to signal an error it relies upon
the nonexistence of certain reports by sheep-herding language
theorists.

-al

;; eiod.scm: eval-in-one-define
;; Copyright 2002 Al Petrofsky <a...@petrofsky.org>
;;
;; A minimal implementation of r5rs eval, null-environment, and
;; scheme-report-environment.

;; You may redistribute and/or modify this software under the terms of
;; the GNU General Public License as published by the Free Software
;; Foundation (fsf.org); either version 2, or (at your option) any
;; later version.

;; Data Structures:

;; An environment is an alist:

;; environment: ((identifier . [value | macro]) ...)
;; macro: (macro-marker macro-sexp macro-env)

;; A value is any arbitrary scheme value. Macros are stored in lists
;; whose car is the eq?-unique macro-marker object.

;; identifier: symbol or (id-marker environment identifier)

;; When a template containing a literal identifier is expanded, the
;; environment of the transformer is added to the identifier, along
;; with the eq?-unique id-marker object.

(define eval
(let ()
(define id-marker (list '*id-marker*))
(define (new-id env id) (list id-marker env id))
(define id-env cadr)
(define id-prev caddr)
(define (id->sym id) (if (symbol? id) id (id->sym (id-prev id))))
(define (id? sexp) (or (symbol? sexp)
(and (pair? sexp) (eq? id-marker (car sexp)))))
(define (spair? sexp) (and (pair? sexp) (not (id? sexp))))
(define (slist? sexp) (or (null? sexp)
(and (spair? sexp) (slist? (cdr sexp)))))

(define (ids->syms sexp)
(cond ((id? sexp) (id->sym sexp))
((pair? sexp) (cons (ids->syms (car sexp))
(ids->syms (cdr sexp))))
((vector? sexp) (list->vector (ids->syms (vector->list sexp))))
(else sexp)))

(define macro-marker (list '*macro-marker*))
(define (vmacro? val) (and (pair? val) (eq? macro-marker (car val))))

(define (new-macro mac-sexp mac-env)
(list macro-marker mac-sexp mac-env))

(define (apply-macro mac sexp env)
(expand-macro sexp env (cadr mac) (caddr mac)))

(define (acons key val alist) (cons (cons key val) alist))

(define (lookup id env)
(or (assq id env)
(if (symbol? id)
id
(lookup (id-prev id) (id-env id)))))

;; place the elements of a list into the first n bindings of env.
(define (mutate-frame env vals)
(do ((env env (cdr env)) (vals vals (cdr vals)))
((null? vals))
(set-cdr! (car env) (car vals))))

(define (eval-body body env)
(let loop ((ienv env) (inits '()) (body body))
(define (finish)
(mutate-frame ienv (map (lambda (init) (xeval init ienv))
inits))
(eval-sequence body ienv))
(define s1 (car body))
(define rest (cdr body))
(if (not (and (spair? s1) (id? (car s1))))
(finish)
(let ((binding (lookup (car s1) ienv)))
(if (symbol? binding)
(case binding
((begin) (loop ienv inits (append (cdr s1) rest)))
((define) (loop (acons (cadr s1) #f ienv)
(cons (caddr s1) inits)
rest))
(else (finish)))
(let ((val (cdr binding)))
(if (vmacro? val)
(loop ienv inits (cons (apply-macro val s1 ienv) rest))
(finish))))))))

(define (eval-lambda vars body env)
(lambda args
(eval-body body (do ((args args (cdr args))
(vars vars (cdr vars))
(env env (acons (car vars) (car args) env)))
((not (spair? vars))
(if (null? vars) env (acons vars args env)))))))

(define (eval-sequence exps env)
;; Don't use for-each because we must tail-call the last expression.
(do ((exp1 (car exps) (car exps))
(exps (cdr exps) (cdr exps)))
((null? exps) (xeval exp1 env))
(xeval exp1 env)))

(define (eval-let-syntax rec? sexp env)
(let* ((body (cddr sexp))
(bindings (cadr sexp))
(keywords (map car bindings))
(transformers (map cadr bindings))
(extended-env (append (map list keywords) env))
(mac-env (if rec? extended-env env)))
(mutate-frame extended-env (map (lambda (x) (new-macro x mac-env))
transformers))
(eval-body body extended-env)))

(define (apply1 combo) (apply (car combo) (cdr combo)))

(define (xeval sexp env)
(let eval-in-this-env ((sexp sexp))
(define (eval-combination) (apply1 (map eval-in-this-env sexp)))
(cond ((id? sexp) (cdr (lookup sexp env)))
((not (spair? sexp)) sexp)
((id? (car sexp))
(let ((binding (lookup (car sexp) env)))
(if (symbol? binding)
(case binding
((get-env) env)
((quote) (ids->syms (cadr sexp)))
((begin) (eval-sequence (cdr sexp) env))
((lambda) (eval-lambda (cadr sexp) (cddr sexp) env))
((set!) (set-cdr! (lookup (cadr sexp) env)
(xeval (caddr sexp) env)))
((let-syntax) (eval-let-syntax #f sexp env))
((letrec-syntax) (eval-let-syntax #t sexp env)))
(let ((val (cdr binding)))
(if (vmacro? val)
(eval-in-this-env (apply-macro val sexp env))
(eval-combination))))))
(else (eval-combination)))))

(define (expand-macro sexp env mac-sexp mac-env)
(define literals (cadr mac-sexp))
(define rules (cddr mac-sexp))

(define (pat-literal? id) (memq id literals))
(define (not-pat-literal? id) (not (pat-literal? id)))

(define (ellipsis? x) (and (id? x) (eq? '... (lookup x env))))
(define (ellipsis-pair? x) (and (spair? x) (ellipsis? (car x))))

(define (free-id=? pat-id sexp-id)
(eq? (lookup pat-id mac-env) (lookup sexp-id env)))

;; List-ids returns a list of those ids in a pattern or template
;; for which (pred? id) is true. If include-scalars is false, we
;; only include ids that are within the scope of at least one
;; ellipsis.
(define (list-ids x include-scalars pred?)
(let collect ((x x) (including include-scalars) (l '()))
(cond ((vector? x) (collect (vector->list x) including l))
((and (id? x) including (pred? x))
(cons x l))
((spair? x)
(if (ellipsis-pair? (cdr x))
(collect (car x) #t
(collect (cddr x) including l))
(collect (car x) including
(collect (cdr x) including l))))
(else l))))

;; Returns #f or an alist mapping each pattern var to a part of
;; the input. Ellipsis vars are mapped to lists of parts (or
;; lists of lists...).
(define (match-pattern pat sexp)
(call-with-current-continuation
(lambda (return)
(define (fail) (return #f))
(let match ((pat pat) (sexp sexp) (bindings '()))
(define (continue-if condition) (if condition bindings (fail)))
(cond
((id? pat)
(if (pat-literal? pat)
(continue-if (and (id? sexp) (free-id=? pat sexp)))
(acons pat sexp bindings)))
((vector? pat)
(or (vector? sexp) (fail))
(match (vector->list pat) (vector->list sexp) bindings))
((not (spair? pat))
(continue-if (equal? pat sexp)))
((ellipsis-pair? (cdr pat))
(or (slist? sexp) (fail))
(append (apply map list (list-ids pat #t not-pat-literal?)
(map (lambda (x)
(map cdr (match (car pat) x '())))
sexp))
bindings))
((spair? sexp)
(match (car pat) (car sexp)
(match (cdr pat) (cdr sexp) bindings)))
(else (fail)))))))

(define (expand-template pat tmpl sexp top-bindings)
(define ellipsis-vars (list-ids pat #f not-pat-literal?))
(define (list-ellipsis-vars subtmpl)
(list-ids subtmpl #t (lambda (id) (memq id ellipsis-vars))))
;; New-literals is an alist mapping each literal id in the
;; template to a fresh id for inserting into the output. It
;; might have duplicate entries mapping an id to two different
;; fresh ids, but that's okay because when we go to retrieve a
;; fresh id, assq will always retrieve the first one.
(define new-literals
(map (lambda (id) (cons id (new-id mac-env id)))
(list-ids tmpl #t (lambda (id) (not (assq id top-bindings))))))
(let expand ((tmpl tmpl) (bindings top-bindings))
(let expand-part ((tmpl tmpl))
(cond
((id? tmpl) (cdr (or (assq tmpl bindings)
(assq tmpl top-bindings)
(assq tmpl new-literals))))
((vector? tmpl) (list->vector (expand-part (vector->list tmpl))))
((spair? tmpl)
(if (ellipsis-pair? (cdr tmpl))
(let ((vars-to-iterate (list-ellipsis-vars (car tmpl))))
(append (apply map
(lambda vals
(expand (car tmpl)
(map cons vars-to-iterate vals)))
(map (lambda (var)
(cdr (assq var bindings)))
vars-to-iterate))
(expand-part (cddr tmpl))))
(cons (expand-part (car tmpl)) (expand-part (cdr tmpl)))))
(else tmpl)))))

(let loop ((rules rules))
(define rule (car rules))
(let ((pat (car rule)) (tmpl (cadr rule)))
(define bindings (match-pattern (cdr pat) (cdr sexp)))
(if bindings
(expand-template (cdr pat) tmpl (cdr sexp) bindings)
(loop (cdr rules))))))

;; We make a copy of the initial input to ensure that subsequent
;; mutation of it does not affect eval's result. [1]
(define (copy x)
(cond ((string? x) (string-copy x))
((pair? x) (cons (copy (car x)) (copy (cdr x))))
((vector? x) (list->vector (copy (vector->list x))))
(else x)))

(lambda (sexp env)
(xeval (copy sexp) env))))


(define null-environment
(let ()
(define macro-bindings
'((quasiquote
(syntax-rules (unquote unquote-splicing quasiquote)
(`,x x)
(`(,@x . y) (append x `y))
((_ `x . d) (cons 'quasiquote (quasiquote (x) d)))
((_ ,x d) (cons 'unquote (quasiquote (x) . d)))
((_ ,@x d) (cons 'unquote-splicing (quasiquote (x) . d)))
((_ (x . y) . d)
(cons (quasiquote x . d) (quasiquote y . d)))
((_ #(x ...) . d)
(list->vector (quasiquote (x ...) . d)))
((_ x . d) 'x)))
(do
(syntax-rules ()
((_ ((var init . step) ...)
end-clause
. commands)
(let loop ((var init) ...)
(cond end-clause
(else (begin #f . commands)
(loop (begin var . step) ...)))))))
(letrec
(syntax-rules ()
((_ ((var init) ...) . body)
(let () (define var init) ... (let () . body)))))
(let*
(syntax-rules ()
((_ () . body) (let () . body))
((_ (first . more) . body)
(let (first) (let* more . body)))))
(let
(syntax-rules ()
((_ ((var init) ...) . body)
((lambda (var ...) . body)
init ...))
((_ name ((var init) ...) . body)
((letrec ((name (lambda (var ...) . body)))
name)
init ...))))
(case
(syntax-rules (else)
((_ (x . y) . clauses)
(let ((key (x . y)))
(case key . clauses)))
((_ key (else . exps))
(begin #f . exps))
((_ key (atoms . exps) . clauses)
(if (memv key 'atoms) (begin . exps) (case key . clauses)))
((_ key) #f)))
(cond
(syntax-rules (else =>)
((_) #f)
((_ (else . exps)) (begin #f . exps))
((_ (x) . rest) (or x (cond . rest)))
((_ (x => proc) . rest)
(let ((tmp x)) (cond (tmp (proc tmp)) . rest)))
((_ (x . exps) . rest)
(if x (begin . exps) (cond . rest)))))
(and
(syntax-rules ()
((_) #t)
((_ test) test)
((_ test . tests) (if test (and . tests) #f))))
(or
(syntax-rules ()
((_) #f)
((_ test) test)
((_ test . tests) (let ((x test)) (if x x (or . tests))))))
(if
(syntax-rules ()
((_ a b) (if* a (lambda () b)))
((_ a b c) (if* a (lambda () b) (lambda () c)))))
(delay
(syntax-rules ()
((_ x) (delay* (lambda () x)))))))
(define (delay* thunk) (delay (thunk)))
(define (if* a b . c) (if (null? c) (if a (b)) (if a (b) ((car c)))))
(define (null-env)
((eval `(lambda (cons append list->vector memv delay* if*)
(letrec-syntax ,macro-bindings
(let-syntax
((define
(syntax-rules ()
((_ (var . args) . body)
(define var (lambda args . body)))
((_ var init)
(define var init)))))
(get-env))))
'())
cons append list->vector memv delay* if*))
(define promise (delay (null-env)))
(lambda (version)
(if (= version 5)
(force promise)
(open-input-file "sheep-herders/r^-1rs.ltx")))))


(define scheme-report-environment
(let-syntax
((extend-env
(syntax-rules ()
((_ env name ...)
((eval '(lambda (name ...) (get-env))
env)
name ...)))))
(let ()
(define (r5-env)
(extend-env (null-environment 5)
eqv? eq? equal?
number? complex? real? rational? integer? exact? inexact?
= < > <= >= zero? positive? negative? odd? even?
max min + * - /
abs quotient remainder modulo gcd lcm numerator denominator
floor ceiling truncate round rationalize
exp log sin cos tan asin acos atan sqrt expt
make-rectangular make-polar real-part imag-part magnitude angle
exact->inexact inexact->exact
number->string string->number
not boolean?
pair? cons car cdr set-car! set-cdr! caar cadr cdar cddr
caaar caadr cadar caddr cdaar cdadr cddar cdddr
caaaar caaadr caadar caaddr cadaar cadadr caddar cadddr
cdaaar cdaadr cdadar cdaddr cddaar cddadr cdddar cddddr
null? list? list length append reverse list-tail list-ref
memq memv member assq assv assoc
symbol? symbol->string string->symbol
char? char=? char<? char>? char<=? char>=?
char-ci=? char-ci<? char-ci>? char-ci<=? char-ci>=?
char-alphabetic? char-numeric? char-whitespace?
char-upper-case? char-lower-case?
char->integer integer->char char-upcase char-downcase
string? make-string string string-length string-ref string-set!
string=? string-ci=? string<? string>? string<=? string>=?
string-ci<? string-ci>? string-ci<=? string-ci>=?
substring string-append string->list list->string
string-copy string-fill!
vector? make-vector vector vector-length vector-ref vector-set!
vector->list list->vector vector-fill!
procedure? apply map for-each force
call-with-current-continuation
values call-with-values dynamic-wind
eval scheme-report-environment null-environment
call-with-input-file call-with-output-file
input-port? output-port? current-input-port current-output-port
with-input-from-file with-output-to-file
open-input-file open-output-file close-input-port close-output-port
read read-char peek-char eof-object? char-ready?
write display newline write-char))
(define promise (delay (r5-env)))
(lambda (version)
(if (= version 5)
(force promise)
(open-input-file "sheep-herders/r^-1rs.ltx"))))))

;; [1] Some claim that this is not required, and that it is compliant for
;;
;; (let* ((x (string #\a))
;; (y (eval x (null-environment 5))))
;; (string-set! x 0 #\b)
;; y)
;;
;; to return "b", but I say that's as bogus as if
;;
;; (let* ((x (string #\1))
;; (y (string->number x)))
;; (string-set! x 0 #\2)
;; y)
;;
;; returned 2. Most implementations disagree with me, however.
;;
;; Note: it would be fine to pass through those strings (and pairs and
;; vectors) that are immutable, but we can't portably detect them.

Todd Gillespie

unread,
May 20, 2002, 6:35:00 PM5/20/02
to
Sander Vesik <san...@haldjas.folklore.ee> wrote:

: <flame-bait>the macros are way more usefull than the numeric tower and
: its warts anyways</flame-bait>

What are the warts of the numeric tower?


: +++ Out of cheese error +++

I get that all the time.


felix

unread,
May 21, 2002, 3:52:32 AM5/21/02
to

David Rush wrote in message ...
>
>It is also not entirely obvious which (if any) program definition are
>present in (interaction-environment). It seems to mostly be the case
>that everything from the program is there, but again, I wouldn't be
>kvetching about this if it hadn't tripped me up. Essentially, I
>concluded that if I wanted portably predictable behavior, that I
>needed to implement my own eval.

Perhaps the implementation(s) you tested were buggy.
I'm not a distinguished R5RS language lawyer, but the meaning
of `interaction-environment' seems clear:

"The intent is that this procedure will return the environment in which the implementation would evaluate expressions
dynamically typed by the user."

This is a little weak, I admit. But the intent (of this sentence) looks obvious
to me. Compilers that do not provide a REPL should try their best to cope
with this situation.

>
>I should add that I am also concerned with the behavior of eval in
>compiled programs which *don't* have the REPL running.
>
>> Why would it be more useful with first-class environments?
>
>Because then you could control what is in each environment. This is
>really useful for sandboxing and namespace control of user-evaluated
>code.
>


(define (eval-in-limited-environment exp env)
((eval `(lambda ,(map car env) ,exp) (null-environment 5))
(map cdr env) ) )

(eval-in-limited-environment
'(begin (go 100) (left 90) (go 100) (left 90))
(list (cons 'go move-turtle) (cons 'left turn-turtle-left)) )


BTW, aren't R5RS environments "first-class"?


cheers,
felix


felix

unread,
May 21, 2002, 5:16:45 AM5/21/02
to

felix wrote in message <3cea02d8$0$14328$9b62...@news.freenet.de>...

>
>(define (eval-in-limited-environment exp env)
> ((eval `(lambda ,(map car env) ,exp) (null-environment 5))
> (map cdr env) ) )
>
>(eval-in-limited-environment
> '(begin (go 100) (left 90) (go 100) (left 90))
> (list (cons 'go move-turtle) (cons 'left turn-turtle-left)) )
>
>

It should of course be:

(define (eval-in-limited-environment exp env)
(apply


(eval `(lambda ,(map car env) ,exp) (null-environment 5))
(map cdr env) ) )

Sorry.


cheers,
felix


David Rush

unread,
May 21, 2002, 6:26:31 AM5/21/02
to
"felix" <felixu...@freenet.de> writes:
> David Rush wrote in message ...
> >
> >It is also not entirely obvious which (if any) program definition are
> >present in (interaction-environment). It seems to mostly be the case
> >that everything from the program is there, but again, I wouldn't be
> >kvetching about this if it hadn't tripped me up. Essentially, I
> >concluded that if I wanted portably predictable behavior, that I
> >needed to implement my own eval.
>
> Perhaps the implementation(s) you tested were buggy.

Perhaps (in the sense that they caused me bugs), but after reading the
R5RS language, I didn't feel that there was sufficient room to complain.

> I'm not a distinguished R5RS language lawyer, but the meaning
> of `interaction-environment' seems clear:
>
> "The intent is that this procedure will return the environment in
> which the implementation would evaluate expressions
> dynamically typed by the user."
>
> This is a little weak, I admit. But the intent (of this sentence)
> looks obvious to me. Compilers that do not provide a REPL should try
> their best to cope with this situation.

I think this language is more than a "little" weak, but that is where
we differ, now isn't it? I agree the intent should be obvious, but
where does it say that two succesive calls to interaction-environment
must return two different environments if there have been any
environment-modifying forms evaluated? *Especially* in a compiled
program, where it may be exceptionally difficult to mutably reify the
environment?

As far as I'm concerned, this reflects *exactly* the issues that had
kept eval out of R<5RS.

> >I should add that I am also concerned with the behavior of eval in
> >compiled programs which *don't* have the REPL running.
> >
> >> Why would it be more useful with first-class environments?
> >
> >Because then you could control what is in each environment. This is
> >really useful for sandboxing and namespace control of user-evaluated
> >code.

> (define (eval-in-limited-environment exp env)
> ((eval `(lambda ,(map car env) ,exp) (null-environment 5))
> (map cdr env) ) )
>
> (eval-in-limited-environment
> '(begin (go 100) (left 90) (go 100) (left 90))
> (list (cons 'go move-turtle) (cons 'left turn-turtle-left)) )

This solution does not scale too terribly well, don't you think? At
least I'm not finding it to do so, and I'm using what amounts to a minor
syntactic variation (I build an explicit let around the expression I
want to evaluate). Hmmm, actually yours might be better because it
more explicitly controls what objects get bound to the lambda-args
rather than trusting eval to work it out.

So what you have done amounts to a work-around. I officially downgrade
my complaint to "mostly useless".

> BTW, aren't R5RS environments "first-class"?

Ok, I'm not Matthias Blume, but I'll do my best impersonation: They
are first-class, but there are no guarantees about their mutability,
nor (as in the case with Scheme48) about what is visible (because of
the module system) even in interaction-environment. There is also
only *one* operator on envirnments (eval) so you have no way of
checking for errors before the evaluation of a datum. Nor do you have
any way of distinguishing one instance of interaction-environment from
another one, or from null-environment or scheme-report-environment for
that matter.

So maybe environments *aren't* really first class since their behavior
w/rt eq? eqv? and equal? isn't specified. How finely do you want to
split the linguistic hair? There are definitely holes in the
specification.

eval also contains an implicit type-error in that it only returns a
*single* value. This means that code like:

(call-with-values
(lambda () (eval '(values 6 9)))
(lambda (l r)
(display `(the ultimate answer is: ,(* 6 9)))))

will (perhaps surprisingly) error.

Digressing a bit then, since Scheme has no way to catch errors (in
general, and from eval in particular) if you allow user code to be
eval'd, you have no way to avoid a core dump when the idiot mistypes.

In short, if you're going to have eval in the language, it's needs to
have an interface like:

(eval datum env k-success k-failure)

where
(define (k-success env . values) ...)
(define (k-failure datum env) ...)

so that you at least have a chance to keep up with the changes caused
by the evaluated datum.

Whew! when I started this message, I thought I was going to need to
point out that I was merely being inflammatory (in
hah-hah-only-serious mode). It turns out that I *am* actually quite
annoyed with the function. At the least it should be dropped from RnRS
and moved into a SRFI.

That's my story, and I'm sticking with it.

david rush
--
The important thing is victory, not persistence.
-- the Silicon Valley Tarot

Al Petrofsky

unread,
May 21, 2002, 12:15:42 PM5/21/02
to
David Rush <ku...@bellsouth.net> writes:
> "felix" <felixu...@freenet.de> writes:

> > "The intent is that this procedure will return the environment in
> > which the implementation would evaluate expressions
> > dynamically typed by the user."

> I think this language is more than a "little" weak, but that is where


> we differ, now isn't it? I agree the intent should be obvious, but
> where does it say that two succesive calls to interaction-environment
> must return two different environments if there have been any
> environment-modifying forms evaluated?

It doesn't. It says that each time, "the environment" for user-typed
expressions is returned. In a typical read-eval-print system, this is
the same mutable environment every time.

> *Especially* in a compiled program, where it may be exceptionally
> difficult to mutably reify the environment?

If there is no "environment in which the implementation would evaluate
expressions dynamically typed by the user", then presumably such a
system will not provide interaction-environment. That's why it's
optional.

> eval also contains an implicit type-error in that it only returns a
> *single* value. This means that code like:
>
> (call-with-values
> (lambda () (eval '(values 6 9)))
> (lambda (l r)
> (display `(the ultimate answer is: ,(* 6 9)))))
>
> will (perhaps surprisingly) error.

There's a typo in section 6.5 where "returns its value(s)" came out
"returns its value". We know this is a typo because section 3.5
explicitly states "eval must evaluate its argument as if it were in
tail position within the eval procedure". That is, if, as in your
example, the call to eval has a binary continuation, then the evaled
expression must also have a binary continuation, because it is
required to be the same continuation.

> Digressing a bit then, since Scheme has no way to catch errors (in
> general, and from eval in particular) if you allow user code to be
> eval'd, you have no way to avoid a core dump when the idiot mistypes.

Systems that implement only the very minimal error handling required
by r5rs are not very practical. This is hardly an issue specific to
eval.

> In short, if you're going to have eval in the language, it's needs to
> have an interface like:
>
> (eval datum env k-success k-failure)

Nothing in r5rs is stopping a system from providing that interface.

> Whew! when I started this message, I thought I was going to need to
> point out that I was merely being inflammatory (in
> hah-hah-only-serious mode). It turns out that I *am* actually quite
> annoyed with the function. At the least it should be dropped from
> RnRS and moved into a SRFI.
>
> That's my story, and I'm sticking with it.

I'm still trying to figure out what your criteria for SRFI-dom are.
Are you saying that SRFIs are for functions that annoy you?

-al

Ray Blaak

unread,
May 21, 2002, 12:18:28 PM5/21/02
to
David Rush <ku...@bellsouth.net> writes:
> The macro-system, while pretty cool, has got holes in it big enough top
> drive a truck through (as Oleg Kiselyov and Al* Petrofsky have done)

Have they? I thought they demonstrated how to implement a non-hygenic macro
system using a hygenic macro system.

That's kind of like using ML to implement a C compiler.

I don't see how an "unsafe" interpretation system says anything about the
safety of the system used to implement it.

--
Cheers, The Rhythm is around me,
The Rhythm has control.
Ray Blaak The Rhythm is inside me,
bl...@telus.net The Rhythm has my soul.

David Rush

unread,
May 21, 2002, 5:59:09 PM5/21/02
to
Al Petrofsky <a...@petrofsky.org> writes:
> What is your idea of what should be in SRFIs?

Official editorial policy is: anything.

My personal esthetics goes more along the lines of anything not in Tom
Lord's "Core Scheme" should be in a SRFI, where Core Scheme is quite a
bit smaller than R5RS. This is still a somewhat inconsistent view,
which seems to me (today, anyway) practical. The real issue is that
there is *no* current RnRS activity: but that's a whole 'nmuther
kettle of fish. All I will say is that there seems (to me) to be a
need for two reports: a Language report and a Library report (IIRC,
like the SML community).

My personal approximation of Core Scheme is something like:

function application
lambda
if
set!
call/cc
numbers
characters
strings
booleans
_|_, which is conventionally denoted by ()
define-record-type (SRFI-9)

> I would think srfis should be for these sorts of things:

Well again, official editorial policy is: anything. I certainly
esthetically agree with all your cases, but I think that they are
incomplete.

> A macro system doesn't fit in there at all.

(obviously) I disagree. syntax-case could (and should) certainly be
written up as a SRFI, and it is arguably *more* fundamental than
syntax-rules.

> On the other hand, having
> a macro system in the standard has the benefit that it enables moving
> things like case, do, and delay to the library.

Well this goes back to the "two reports" idea, which I favor. However
you don't *need* to have a macro specification to consider case, do
and delay, to be library components. It simply makes their exposition
that much easier.

> If the standard included a simple low-level macro system with
> primitives for effecting hygiene (including referential transparency
> and hygienic literals matching), then it could make sense for the
> pattern/template language of syntax-rules (and syntax-case) to be
> specified outside of the main standard. Is that what you mean?

I think that such a macro system *needs* to be implemented. I still
don't think of it as part of Core Scheme. Given that Oleg (mostly) and
you have shown that syntax-rules allows the writing of non-hygienic
macros, I am inclined to wonder about its mindshare cost/benefit
ratio. Don't get me wrong, now that I have begun to grasp the
syntax-nature I use them quite regularly (and will do so even more
once I get around to implementing the description in your _Primer for
the Insane_).

I know that I'm going to be wildly misunderstood here. I actually
*like* syntax-rules. But it is clearly only one of many possible macro
systems and syntax of define-syntax implicitly acknowledges that
fact. Because there *isn't* One Right Way, I don't think that the
standard should specify *any* specific way. My position about macro
systems and SRFI-land follows trivially.

> > Marc Feeley is one of the RnRS-editors/authors.
> A look at the first page of an rnrs indicates this to be false:

Sorry editors, I should have checked first. I hadn't actually expected
to be taken very seriously when I made my original comments and had
inserted a disclaimer to that effect. R5RS Scheme is for me much like
a house that I have bought. I'm quite happy with it, but there's also a
whole bunch of stuff that I feel needs modified. Having watched the c.l.s
traffic over the last couple of years, I don't think I'm alone in that
opinion.

david rush
--
Next to the right of liberty, the right of property is the most
important individual right guaranteed by the Constitution and the one
which, united with that of personal liberty, has contributed more to
the growth of civilization than any other institution established by
the human race.
-- Popular Government (William Howard Taft)

David Rush

unread,
May 21, 2002, 6:06:15 PM5/21/02
to
Ray Blaak <bl...@telus.net> writes:
> David Rush <ku...@bellsouth.net> writes:
> > The macro-system, while pretty cool, has got holes in it big enough top
> > drive a truck through (as Oleg Kiselyov and Al* Petrofsky have done)
>
> Have they? I thought they demonstrated how to implement a non-hygenic macro
> system using a hygenic macro system.

Except that the whole point of the thing was to prevent such
shenanigans. To me that makes it fairly broken (in addition to being a
Turing victim).

> That's kind of like using ML to implement a C compiler.

Somewhere out there in Usenet-land a Matthias is screaming...

> I don't see how an "unsafe" interpretation system says anything about the
> safety of the system used to implement it.

Our understandings of the [AO]l* results may not be isomorphic, in
which case I wouldn't be surprised if I were wrong on some points, but
in very simple terms if you set out to prevent something and that
something occurs then you have failed.

And anyway, I *still* _like_ syntax-rule macros. I just don't think
they deliver what was advertised. And they are a great demostration of
the principle that it's very hard to write a useful, but *limited*
computer language.

david rush
--
Scheme: Because closures are cool.
-- Anton van Straaten (the Scheme Marketing Dept from c.l.s)

Rob Warnock

unread,
May 22, 2002, 12:16:40 AM5/22/02
to
Jens Axel Søgaard <use...@soegaard.net> wrote:
+---------------
+---------------

Those directions work for v.103, too.

Note that on MIPS/Irix platforms (and, presumably, others that
support dynamic loading of shared libraries), you can pre-link the
"libgc.a" & "libmzscheme.a" together into a single "libmzscheme.so"
[or separate .so's, whichever], which makes executables which embed
MzScheme quite small. E.g., the executable for the test program given
in the above URL ended up being only ~22 KB (stripped) on Irix 6.5.5:

% % cc -g -o foo -rpath $cwd -I../../include foo.c -L. -lmzscheme
% ls -l foo
-rwxr-xr-x 1 rpw3 engr 22020 May 21 21:10 foo
% /bin/time ./foo '(+ 12 34)'
46
real 0.211, user 0.130, sys 0.051
%

Not as fast-starting as Perl or /bin/sh, perhaps, but good enough
for most interactive uses...


-Rob

-----
Rob Warnock, 30-3-510 <rp...@sgi.com>
SGI Network Engineering <http://reality.sgiweb.org/rpw3/>
1600 Amphitheatre Pkwy. Phone: 650-933-1673
Mountain View, CA 94043 PP-ASEL-IA

[Note: aaan...@sgi.com and zedw...@sgi.com aren't for humans ]

felix

unread,
May 22, 2002, 4:16:58 AM5/22/02
to

David Rush wrote in message ...

>


>> (define (eval-in-limited-environment exp env)
>> ((eval `(lambda ,(map car env) ,exp) (null-environment 5))
>> (map cdr env) ) )
>>
>> (eval-in-limited-environment
>> '(begin (go 100) (left 90) (go 100) (left 90))
>> (list (cons 'go move-turtle) (cons 'left turn-turtle-left)) )
>
>This solution does not scale too terribly well, don't you think? At
>least I'm not finding it to do so, and I'm using what amounts to a minor
>syntactic variation (I build an explicit let around the expression I
>want to evaluate). Hmmm, actually yours might be better because it
>more explicitly controls what objects get bound to the lambda-args
>rather than trusting eval to work it out.


The scalability issue is something different, I'd say.
It might be sufficient for many applications.
That's different to "completely useless". It certainly doesn't *have*
to be slow.

>
>So what you have done amounts to a work-around. I officially downgrade
>my complaint to "mostly useless".


Ok. Accepted. ;-)

>
>> BTW, aren't R5RS environments "first-class"?
>
>Ok, I'm not Matthias Blume, but I'll do my best impersonation: They
>are first-class, but there are no guarantees about their mutability,
>nor (as in the case with Scheme48) about what is visible (because of
>the module system) even in interaction-environment. There is also
>only *one* operator on envirnments (eval) so you have no way of
>checking for errors before the evaluation of a datum. Nor do you have
>any way of distinguishing one instance of interaction-environment from
>another one, or from null-environment or scheme-report-environment for
>that matter.


That's true, but keep in mind the there is only *one* operation on
procedures (application) and there is no way of checking for errors
before invoking it. And equality of procedures isn't entirely clear either...

>
>So maybe environments *aren't* really first class since their behavior
>w/rt eq? eqv? and equal? isn't specified. How finely do you want to
>split the linguistic hair? There are definitely holes in the
>specification.


Loads of 'em.

>
>Digressing a bit then, since Scheme has no way to catch errors (in
>general, and from eval in particular) if you allow user code to be
>eval'd, you have no way to avoid a core dump when the idiot mistypes.


Right. But again this can be acceptable, depending on the application.
And writing scripts may be reserved for the maintainer of the application.
The scripting language might even be restrained to simple configuration
options:

(host-name "you.are.here")
(number-of-columns 80)
(number-of-rows 25)
(number-of-characters (+ (number-of-rows) (number-of-columns)))
(user-name "felix")
...

(`load' is optional. So it may not be available)

I agree that R5RS `eval' is to flimsy to write your own interactive
REPL, with all bells and whistles like error-recovery and the likes.

Yet it can be more than sufficient (IMHO) for several handy things, like
configuration scripts, command processors, data-driven code,
or dynamically generated code (Genetic Programming, etc.).


cheers,
felix


Richard Kelsey

unread,
May 22, 2002, 10:33:30 AM5/22/02
to
Ray Blaak <bl...@telus.net> wrote in message news:<uvg9hxyn...@telus.net>...

> David Rush <ku...@bellsouth.net> writes:
> > The macro-system, while pretty cool, has got holes in it big enough top
> > drive a truck through (as Oleg Kiselyov and Al* Petrofsky have done)
>
> Have they? I thought they demonstrated how to implement a non-hygenic macro
> system using a hygenic macro system.

As far as I can tell, they haven't demonstrated anything of the sort.
I think the confusion is about how (hygenic) keywords work in R5RS
macros. In (hooray for Google):

http://groups.google.com/groups?hl=en&lr=&selm=87oflzcdwt.fsf%40radish.petrofsky.org

Algol Petrofsky wrote:

> A commonly cited example of a macro that cannot be written with
> syntax-rules is a loop macro that implicitly binds an identifier
> to an exit procedure.

and then defines a macro for (loop <expression> ...) that, he claims:

> causes the expressions to be evaluated in an infinite loop, with
> the variable break bound to a procedure that will exit the loop.

His awe-inspiring LOOP macro does what he claims, but not by binding
the variable BREAK. Instead it makes use of a keyword BREAK, exactly
as intended by the macro system. A correct description would be:

causes the expressions to be evaluated in an infinite loop,
with the keyword break expanding to a procedure that will exit
the loop.

Nothing unhygenic about it, as a few examples show:

(loop (break (let ((break -))
(break 4))))
=> -4

(let ((break -))
(loop (break 4)))
=> loops indefinitely

(loop (let-syntax ((f (syntax-rules ()
((f x) (break x)))))
(loop (break (- (f 4))))))
=> 4

One could make the argument that keywords are themselves unhygenic,
but that doesn't seem to be what is being claimed.

-Richard Kelsey

Ray Blaak

unread,
May 22, 2002, 12:13:34 PM5/22/02
to
kel...@s48.org (Richard Kelsey) writes:
> Ray Blaak <bl...@telus.net> wrote in message news:<uvg9hxyn...@telus.net>...
> > [...] I thought they demonstrated how to implement a non-hygenic macro

> > system using a hygenic macro system.
>
> As far as I can tell, they haven't demonstrated anything of the sort.

[...cool stuff about a loop macro elided...]

I was referring to "How to write dirty R5RS macros". See (hooray again for
Google):

http://groups.google.com/groups?hl=en&lr=&ie=UTF8&oe=UTF8&q=How+to+write+dirty+R5RS+macros&btnG=Google+Search

Alleluia Petrofsky

unread,
May 22, 2002, 1:21:31 PM5/22/02
to
kel...@s48.org (Richard Kelsey) writes:
> Algol Petrofsky wrote:
>
> > A commonly cited example of a macro that cannot be written with
> > syntax-rules is a loop macro that implicitly binds an identifier
> > to an exit procedure.
>
> and then defines a macro for (loop <expression> ...) that, he claims:
>
> > causes the expressions to be evaluated in an infinite loop, with
> > the variable break bound to a procedure that will exit the loop.
>
> His awe-inspiring LOOP macro does what he claims, but not by binding
> the variable BREAK. Instead it makes use of a keyword BREAK, exactly
> as intended by the macro system.

Part of the difficulty of being very precise about these things is
lack of good nomenclature. In r5rs, "keyword" is used to mean both an
identifier with a syntax binding, and an identifier like ELSE that is
recognized specially by some syntax binding. I call the latter case
"pattern literals". They may be keywords or variables, or even be
completely unbound:

(let-syntax ((l (syntax-rules ())))
(let-syntax ((m (syntax-rules (l) ((_ l) 1))))
(m l))) => 1

(let ((l #f))
(let-syntax ((m (syntax-rules (l) ((_ l) 1))))
(m l))) => 1

(let-syntax ((m (syntax-rules (l) ((_ l) 1))))
(m l)) => 1

(Aside: an unused macro of the form (syntax-rules ()) is legal on
non-Blume compilers.)

> A correct description would be:
>
> causes the expressions to be evaluated in an infinite loop,
> with the keyword break expanding to a procedure that will exit
> the loop.

Occurrences of the pattern literal break do not expand into a
procedure. In fact, procedures aren't in r5rs macros' domain or
range, which comprise only pairs, vectors, strings, numbers,
characters, identifiers, booleans, and nulls.

What occurrences of break expand into is themselves. Also in the
output is a copy of one of the break identifiers in the formal
parameter position of a lambda. During the execution of the code,
there will be a "variable break bound to a procedure that will exit
the loop", just as I claimed.

This is unhygienic if you consider it to be "insert[ing] a binding for
an identifier" in the r5rs section 4.3 not-very-precise definition of
hygiene. However, the only interpretation of the definition that
really makes sense is that a macro is considered to have inserted a
binding iff it inserted (using a template literal) the identifier that
gets bound. That's why r5rs implementations interpret the loop macro
the way they do, and that's why I only called it "seemingly
unhygienic".

David Rush wrote:

> in very simple terms if you set out to prevent something and that
> something occurs then you have failed.

What was set out to be prevented was "inadvertent captures" [r5rs
4.3.2]. Syntax-rules is wildly successful at that. Thwarting
deliberate captures was not a design goal. The seminal papers on
hygiene all refer to deliberate capture as an occasionally useful and
legitimate technique, not an evil to be extinguished.

Oleg and I have been debating in email what the larger significance of
our findings is. I tend to think it is not a whole lot. The lesson
of the loop macro is that it's easy to take a vague definition of
hygiene and wrongly conclude that some macro cannot be written
hygienicly. Once you nail down the definition, it becomes more
apparent what is and is not possible.

In March, Oleg demonstrated how to write m and let macros that can be
used like this:

(define i 1)
(m) => 1
(set! i 2)
(m) => 2
(let ((i 3)) (m)) => 3
(let ((i 3)) (- (m))) => -3

The appearance is that the m macro makes an opaque reference to the
binding of i at the point of call. Actually, m is an ordinary macro
that transparently refers to the top-level i. The strangeness is in
let, which finds and copies an m in its input and uses it to form a
local binding for m to a macro that transparently refers to the local
binding for i.

To me, the lesson here is just "don't try to understand a let
expression when you don't know what let's binding is". More
generally, having lexically scoped macros doesn't make it impossible
to write obfuscated macros any more than having lexically scoped
variables makes it impossible to write obfuscated core scheme
programs. With or without macros, scheme is Turing-complete, which
means it is also obfuscation-complete: every obfuscated program ever
written in any language that can be implemented on a Turing machine
can be translated to a scheme program of equal or greater obfuscation
by combining an interpreter for the other language, written in scheme,
with the source of the obfuscated program, transliterated in some way
to a scheme literal.

Again, this is irrelevant to the goal of macro hygiene: the prevention
of *inadvertent* captures.

-al

Richard Kelsey

unread,
May 23, 2002, 6:02:18 AM5/23/02
to
Alleluia Petrofsky <alle...@petrofsky.org> wrote in message news:<874rh0p...@radish.petrofsky.org>...

> Part of the difficulty of being very precise about these things is
> lack of good nomenclature. In r5rs, "keyword" is used to mean both an
> identifier with a syntax binding, and an identifier like ELSE that is
> recognized specially by some syntax binding. I call the latter case
> "pattern literals".

Right. I'll try to do better.

kel...@s48.org (Richard Kelsey) writes:

> > A correct description would be:
> >
> > causes the expressions to be evaluated in an infinite loop,
> > with the keyword break expanding to a procedure that will exit
> > the loop.

Alleluia Petrofsky replies:

> What occurrences of break expand into is themselves. Also in the
> output is a copy of one of the break identifiers in the formal
> parameter position of a lambda. During the execution of the code,
> there will be a "variable break bound to a procedure that will exit
> the loop", just as I claimed.

And during the expansion process the 'break' variable is
recognized using a 'break' pattern literal, which is what I
claimed (or at least what I was trying to claim). The
apparent lack of hygiene is achieved via a pattern literal.
There is a disturbing lack of modularity about them.

In any case, my main point was that there was nothing essentially
unhygenic about the LOOP macro, and we seem to be in agreement on
that.
-Richard Kelsey

Al Petrofsky

unread,
May 23, 2002, 3:24:25 PM5/23/02
to
kel...@s48.org (Richard Kelsey) writes:

> In any case, my main point was that there was nothing essentially
> unhygenic about the LOOP macro, and we seem to be in agreement on
> that.

We are, although it depends a bit on what you mean by "essential".
Loop's operation is within the precise definition of hygiene that can
be inferred from the imprecise one in r5rs, as evidenced by the fact
that it works on most r5rs implementations (which represent their
authors' best guesses at what r5rs means).

-al

P.S. Another problem with the nomenclature is that "hygiene" is too
difficult to spell. When researching hygienic macros on google, it
took me a while to realize that I was missing several interesting
things that had been written on the intimately related topic of
"hygenic" macros.

Sander Vesik

unread,
May 23, 2002, 9:14:40 PM5/23/02
to
David Rush <ku...@bellsouth.net> wrote:
> Al Petrofsky <a...@petrofsky.org> writes:
>> What is your idea of what should be in SRFIs?
>
> Official editorial policy is: anything.
>
> My personal esthetics goes more along the lines of anything not in Tom
> Lord's "Core Scheme" should be in a SRFI, where Core Scheme is quite a
> bit smaller than R5RS. This is still a somewhat inconsistent view,
> which seems to me (today, anyway) practical. The real issue is that
> there is *no* current RnRS activity: but that's a whole 'nmuther
> kettle of fish. All I will say is that there seems (to me) to be a
> need for two reports: a Language report and a Library report (IIRC,
> like the SML community).
>
> My personal approximation of Core Scheme is something like:
>
> function application
> lambda
> if
> set!
> call/cc
> numbers
> characters
> strings
> booleans
> _|_, which is conventionally denoted by ()
> define-record-type (SRFI-9)

No vectors???!!! Having vectors is far more important than having records. Once you
have vectors you can more or less portably have records. Vectors (n-tuples of
locations) are considerably more fundamental than records.

[snip]

>
> I know that I'm going to be wildly misunderstood here. I actually
> *like* syntax-rules. But it is clearly only one of many possible macro
> systems and syntax of define-syntax implicitly acknowledges that
> fact. Because there *isn't* One Right Way, I don't think that the
> standard should specify *any* specific way. My position about macro
> systems and SRFI-land follows trivially.

the numeric tower (and system) is only one of many possible numeric towers/systems
and arguably specifying a different one may have its advantages. Should we thus
not specify any at all?

>
> david rush

--
Sander

David Rush

unread,
May 23, 2002, 11:13:56 PM5/23/02
to
Sander Vesik <san...@haldjas.folklore.ee> writes:
> David Rush <ku...@bellsouth.net> wrote:
> > My personal approximation of Core Scheme is something like:
> >
> > function application
> > lambda
> > if
> > set!
> > call/cc
> > numbers
> > characters
> > strings
> > booleans
> > _|_, which is conventionally denoted by ()
> > define-record-type (SRFI-9)
>
> No vectors???!!!

Oops. And I said "approximation".

> > *like* syntax-rules. But it is clearly only one of many possible macro
> > systems and syntax of define-syntax implicitly acknowledges that
> > fact. Because there *isn't* One Right Way
>

> the numeric tower (and system) is only one of many possible numeric
> towers/systems and arguably specifying a different one may have its
> advantages. Should we thus not specify any at all?

That's a different cat entirely. Your analogy is unsound. You can
hardly compute *anything* without *some* model of numbers. There's a
whole lot of computing that can be done without a macro system.

It's clearly the season of the straw-man on c.l.s...Please try to
remember, I raised the issues in my original post to show arguments
that could be made. In fact I wrote words to the effect of: "Just for
the sake of contentiousness..." I don't think that any of my points
have been conclusively improved in R5RS, and in fact the differences
from R4 -> R5 are not large. And my only point about syntax-rules is
that it would be better of living in a secondary specification. Feel
freed to disagree.

david rush
--
They that can give up essential liberty to obtain a little temporary
safety deserve neither liberty nor safety.
-- Benjamin Franklin

Sander Vesik

unread,
May 25, 2002, 2:34:14 PM5/25/02
to
David Rush <ku...@bellsouth.net> wrote:
>> > *like* syntax-rules. But it is clearly only one of many possible macro
>> > systems and syntax of define-syntax implicitly acknowledges that
>> > fact. Because there *isn't* One Right Way
>>
>> the numeric tower (and system) is only one of many possible numeric
>> towers/systems and arguably specifying a different one may have its
>> advantages. Should we thus not specify any at all?
>
> That's a different cat entirely. Your analogy is unsound. You can
> hardly compute *anything* without *some* model of numbers. There's a
> whole lot of computing that can be done without a macro system.

Sure. You need a basic model of numbers, or more spcificly, something that
can pass as integers. Thats all. You can build the others on top of that
(if you really want). And you do all computation you want. And having a
model for numbers is far cry from the scheme numeric tower.

> that it would be better of living in a secondary specification. Feel
> freed to disagree.

But surely so could the different views into "advanced numerics", including
exact/inexact, and exactly what to include. And these can just as well live
in a separate spec. And as a result, All the things that presently don't really
fit or would go against the spec (mod p / mod 2^n numbers, saturating arithmetic,
etc) could live as their own srfis and be numbers, not just cast-asides.

'We should/could have lots of numeric towers specified in SRFIs' is not
a strawman.

David Rush

unread,
May 26, 2002, 5:10:06 PM5/26/02
to
Sander Vesik <san...@haldjas.folklore.ee> writes:
> David Rush <ku...@bellsouth.net> wrote:
> > (syntax-rules) it would be better of living in a secondary

> > specification. Feel freed to disagree.
>
> But surely so could the different views into "advanced numerics", including
> exact/inexact, and exactly what to include. And these can just as well live
> in a separate spec. And as a result, All the things that presently
> don't really fit or would go against the spec (mod p / mod 2^n
> numbers, saturating arithmetic, etc) could live as their own srfis
> and be numbers, not just cast-asides.

Ooooh! hoisted by me own petard!

> 'We should/could have lots of numeric towers specified in SRFIs' is not
> a strawman.

You're one-hundred percent right. And in fact I favor standardizing
some important (but missing) arithmetics in Scheme, preferably in
RnRS, but I would be happy enough with a SRFI specification.

This is different from the assertion you appeared to make: that the
absence of a macro spec was eqv? to the absence of a numeric spec. I
just don't think so. Church numerals are *many* orders of magnitude
harder to program with than is a macro-less Scheme. If you haven't
already, just check out UnLambda.

david rush
--
In no other country in the world is the love of property keener or
more alert than in the United States, and nowhere else does the
majority display less inclination toward doctrines which in any way
threaten the way property is owned.
-- Democracy in America (Alexis de Tocqueville)

Sander Vesik

unread,
May 26, 2002, 6:56:23 PM5/26/02
to
David Rush <ku...@bellsouth.net> wrote:

> Sander Vesik <san...@haldjas.folklore.ee> writes:
>> 'We should/could have lots of numeric towers specified in SRFIs' is not
>> a strawman.
>
> You're one-hundred percent right. And in fact I favor standardizing
> some important (but missing) arithmetics in Scheme, preferably in
> RnRS, but I would be happy enough with a SRFI specification.
>
> This is different from the assertion you appeared to make: that the
> absence of a macro spec was eqv? to the absence of a numeric spec. I
> just don't think so. Church numerals are *many* orders of magnitude
> harder to program with than is a macro-less Scheme. If you haven't
> already, just check out UnLambda.

Only if by 'lack of numeric spec' to mean that you have to give up integers
aswell. Absence of a macro spec is the eqv? of absence of an advanced
numeric spec. The part you really need is:

begin quote

An implementation of Scheme must support exact integers throughout the
range of numbers that may be used for indexes of lists, vectors, and
strings or that may result from computing the length of a list, vector,
or string. The length, vector-length, and string-length procedures must
return an exact integer, and it is an error to use anything but an exact
integer as an index. Furthermore any integer constant within the index
range, if expressed by an exact integer syntax, will indeed be read as
an exact integer, regardless of any implementation restrictions that may
apply outside this range. Finally, the procedures listed below will always
return an exact integer result provided all their arguments are exact
integers and the mathematically expected result is representable as an
exact integer within the implementation:

+ - *
quotient remainder modulo
max min abs
numerator denominator gcd
lcm floor ceiling
truncate round rationalize
expt

end quote

With appropriate removal of numerator, denominator, floor, ceiling,
truncate, round and rationalize and definitions of what the remaining
do. And the remaining set can be pruned to be even smaller in principle.

Reply all
Reply to author
Forward
0 new messages