On Sat, 2009-04-25 at 18:38 +0200, Michele Simionato wrote:
> On Sat, Apr 25, 2009 at 5:12 PM, Michele Simionato
> <[email protected]> wrote:
> > On Sat, Apr 25, 2009 at 4:50 PM, Abdulaziz Ghuloum <[email protected]>
> > wrote:
> >> [PS. your definition of assert-distinct should use bound-identifier=?
> >> instead of free-identifier=?, unless I misunderstood what it's used
> >> for. It is for checking duplicates in binding forms, like lambda, let
> >> and letrec, right?]
> >>
> > The use case I had in mind was not a let form, but a multi-define form
> > like this one:
> >
> > (def-syntax (multi-define (name1 name2 ...) (value1 value2 ...))
> > #'(begin (define name1 value1) (define name2 value2) ...))
>
> Now I remember that I had even another use case in mind, i.e. an enumeration
> macro, to be shown in future episodes:
>
> $ cat enum.ss
> (import (rnrs) (sweet-macros) (aps list-utils))
>
> (def-syntax (enum name ...)
> (with-syntax (((i ...) (range (length #'(name ...)))))
> #'(syntax-match (name ...)
> (sub (ctx name) #'i) ...))
> (distinct? free-identifier=? #'(name ...))
> (syntax-violation 'enum "Duplicate name" #'(name ...)))
>
> (def-syntax color (enum red green blue))
>
> (display (list (color red) (color green) (color blue))); => (0 1 2)
>
> ;;(def-syntax color (enum red red blue)) ; raises a syntax-violation
>
>
> Is free-identifier=? right in this use case?
IMO, no, because an enclosing binding for an enum'ed name will prevent
it from being recognized, and because different names bound to the same
thing will confuse the enum macro. Also, bound-identifier=? is not
right either. I think when you fully understand the below examples,
you'll have a good understanding of the issues.
$ cat stuff.sls
(library (stuff)
(export foo unique? enumerate name=?)
(import (rnrs))
(define foo 1)
(define (unique? id=?)
(lambda (ids)
(or (null? ids)
(and (not (memp (lambda (x) (id=? x (car ids)))
(cdr ids)))
(unique? (cdr ids))))))
(define (enumerate l)
(let loop ((n (- (length l) 1)) (accum '()))
(if (negative? n)
accum
(loop (- n 1) (cons n accum)))))
(define (name=? x y)
(define (->string z)
(cond ((identifier? z) (symbol->string (syntax->datum z)))
((symbol? z) (symbol->string z))))
(string=? (->string x) (->string y)))
)
$ cat use-enum.sps
(import (rnrs)
(stuff)
(rename (only (stuff) foo) (foo bar)))
(define-syntax show
(syntax-rules ()
((_ expr)
(begin (write 'expr) (display " => ") (write expr) (newline)))))
(define-syntax enum/free-identifier=?
(lambda (stx)
(syntax-case stx ()
((_ name ...)
((unique? free-identifier=?) #'(name ...))
(with-syntax (((i ...) (enumerate #'(name ...))))
#'(lambda (stx)
(syntax-case stx (name ...) ;; uses free-identifier=?
((_ name) i)
...
(_ #''unknown)))))
(_ #'(lambda (_) #''duplicates)))))
(define-syntax color (enum/free-identifier=? red green blue))
(show (color blue))
(show (let ((red 'bound))
(color red)))
(show (let-syntax ((thing (enum/free-identifier=? foo bar)))
(thing ignored)))
(newline)
(define-syntax enum/bound-identifier=?
(lambda (stx)
(syntax-case stx ()
((_ name ...)
((unique? bound-identifier=?) #'(name ...))
(with-syntax (((i ...) (enumerate #'(name ...))))
#'(lambda (stx)
(syntax-case stx ()
((_ x) (bound-identifier=? #'x #'name) i)
...
(_ #''unknown)))))
(_ #'(lambda (_) #''duplicates)))))
(define-syntax size (enum/bound-identifier=? small medium large))
(show (size medium))
(show (let-syntax ((thing (enum/bound-identifier=? foo bar)))
(thing bar)))
(show (let-syntax ((enum/introduce
(syntax-rules ()
((_ x) (enum/bound-identifier=? x introduced)))))
(let-syntax ((thing
;; This should not work because of duplicates but it does
;; work because bound-identifier=? was used.
(enum/introduce introduced)))
(thing introduced))))
(newline)
(define-syntax enum/name=?
(lambda (stx)
(syntax-case stx ()
((_ name ...)
((unique? name=?) #'(name ...))
(with-syntax (((i ...) (enumerate #'(name ...))))
#'(lambda (stx)
(syntax-case stx ()
((_ x) (name=? #'x 'name) i)
...
(_ #''unknown)))))
(_ #'(lambda (_) #''duplicates)))))
(define-syntax shape (enum/name=? round box))
(show (shape box))
(show (let ((round 'bound))
(shape round)))
(show (let-syntax ((thing (enum/name=? foo bar)))
(thing bar)))
(show (let-syntax ((enum/introduce
(syntax-rules ()
((_ x) (enum/name=? x introduced)))))
(let-syntax ((thing
;; This should not work and it does not.
(enum/introduce introduced)))
(thing ignored))))
$
$ ikarus --r6rs-script use-enum.sps
(color blue) => 2
(let ((red 'bound)) (color red)) => unknown
(let-syntax ((thing (enum/free-identifier=? foo bar))) (thing ignored)) =>
duplicates
(size medium) => unknown
(let-syntax ((thing (enum/bound-identifier=? foo bar))) (thing bar)) => unknown
(let-syntax ((enum/introduce (syntax-rules () ((_ x) (enum/bound-identifier=? x
introduced))))) (let-syntax ((thing (enum/introduce introduced))) (thing
introduced))) => unknown
(shape box) => 1
(let ((round 'bound)) (shape round)) => 0
(let-syntax ((thing (enum/name=? foo bar))) (thing bar)) => 1
(let-syntax ((enum/introduce (syntax-rules () ((_ x) (enum/name=? x
introduced))))) (let-syntax ((thing (enum/introduce introduced))) (thing
ignored))) => duplicates
$
For things like cond which need to distinguish macro-specific literals
from bound names (e.g.: (let ((else 1)) (cond (else ---)))),
free-identifier=? is the right predicate. But if your literals should
always be recognized regardless, then symbolic/spelling comparison is
the right thing. I believe syntax-case has its literals clause which
uses free-identifier=? because that is the safest default for people who
don't understand all these identifiers issues.
--
: Derick
----------------------------------------------------------------