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

Reply via email to