Sean, it took me a while to figure out condlet but here is how a Racketeer would write this (questionable) macro:
(define-syntax (condlet stx) (syntax-case stx () [(condlet ((c (x e) ...) ...) body ...) #'(cond [c (let* ((x '()) ... ...) (let ((x e) ...) body ...))] ...)])) As Stephan points out, a let* suffices here because it simply doesn't matter because it simply doesn't matter which x binding body ... sees. For your information, you can also write (define-syntax (condlet stx) (syntax-case stx () [(condlet ((c (x e) ...) ...) body ...) (let* ((vars (remove-duplicates #'(x ... ...))) (nils (map (lambda (c) #''()) vars))) #`(cond [c ((lambda #,vars (let ((x e) ...) body ...)) #,@nils)] ...))])) (define-for-syntax (remove-duplicates vars) (let loop ((vars (syntax->list vars)) (seen '())) (cond [(null? vars) seen] [(memf (lambda (s) (free-identifier=? (car vars) s)) seen) (loop (cdr vars) seen)] [else (loop (cdr vars) (cons (car vars) seen))]))) The define-for-syntax form introduces functions usable during the syntax phase. That keeps your syntax definitions small. See style guide. Here are the tests I ran (module+ test (require rackunit) (define (princ s) (displayln s) s)) (module+ test (check-equal? (let ((x 1)) (condlet (((= x 2) (x (princ 'a)) (y (princ 'b))) ((= x 1) (y (princ 'c)) (x (princ 'd))) (else (x (princ 'e)) (z (princ 'f)))) (list x y z))) (list 'd 'c '()))) -- Matthias On Jan 1, 2013, at 5:59 PM, Sean Kanaley wrote: > While I've ultimately succeeded in having it return the correct output for a > sample input, I'm not positive it's correct in general and I am positive it's > written poorly, as I don't fully understand both syntax-case and syntax > objects vs. datums. If someone could look it over and provide a more > canonical version, I would be grateful. > > Here's how the macro works: > > > (condlet (((= 1 2) (x (princ ’a)) (y (princ ’b))) > ((= 1 1) (y (princ ’c)) (x (princ ’d))) > (t (x (princ ’e)) (z (princ ’f)))) > (list x y z)) > CD > (D C NIL) > > Before I post the horrible racket code, I will explain the problems I'm > having with macros in general: > > Problem 1, separate phases: I have a remove-duplicates-by function that > would be great to have globally, but it seemingly must be written locally. > > Problem 2: You can't use a pattern variable outside of a pattern, so you have > to syntax-ify it with #', but then you can't access the associated s-exp > without removing the syntax. The way to bind things to null by default is to > get every id and output the obvious let statement, except ids might be > repeated so you have to remove duplicates (enter problem 1). It's > remove-duplicates-BY because the removal happens by syntax->datum'ing each > identifier-syntax-thing since it can't appear outside of a pattern. > > Problem 2: How to remove a portion of the macro code into a separate > transformer function? It's kind of annoying having a whole block of code > relegated to cleaning up the duplicate ids inside of the let it expands into. > That would ideally be written "let #,(remove-dups #'(c cs...))" or > similar...some kind of sub-macro to handle just getting ids. I thought > that's what let-syntax or nested define syntaxes were for but I get phase > errors or preposterous, very dark errors like "lambda not bound". Suddenly I > prefer Haskell's a is not an infinitely existential StateT (Bool -> IO > (StateT (Cont String) (Cont String) ())) Maybe (a1,a1'), in subexpression f . > g. Oh, f <$> g. everything checks out now! Thanks, ghci, and by the way go > **** yourself you stupid Cont. > > Anywayyyyy here is my code that works for the above example at least: > > (define-syntax (condlet s) > (let ((remove-duplicates-by > (λ (f l) (let R ((l l)) > (if (null? l) > null > (cons (car l) (R (remove* (list (car l)) > (cdr l) > (λ (a b) > (eq? (f a) (f b))))))))))) > (syntax-case s () > ((_ (c) body ...) > (syntax-case #'c (else) > ((else binds ...) > #'(let (binds ...) body ...)) > ((t binds ...) > #'(if t (let (binds ...) body ...) (void))))) > ((_ (c cs ...) body ...) > (syntax-case #'c () > ((t binds ...) > #`(let #,(syntax-case #'(c cs ...) () > (((_ (i _) ...) ...) > (map (λ (i) #`(#,i null)) > (remove-duplicates-by > syntax->datum > (syntax->list #'(i ... ...)))))) > (if t > (let (binds ...) body ...) > (condlet (cs ...) body ...))))))))) > > Any help is appreciated. > ____________________ > Racket Users list: > http://lists.racket-lang.org/users
____________________ Racket Users list: http://lists.racket-lang.org/users