Here's a case for your consideration. Recently, I wanted to write a macro which looked like 'dolist' but behaved like 'map'. I decided to call it 'collect', and I wanted its usage to be such that I could do the following, for instance.
Code: Select all
> (collect (x '(1 2 3) y '(4 5 6)) (list y x))
((4 1) (5 2) (6 3))
> (collect (x '(1 2 3) y '(4 5 6)) (println y) x)
4
5
6
(1 2 3)
Code: Select all
`(map (fn ,vars ,@body) ,@lists)
The following definition is as close as I could get.
Code: Select all
(define-macro (collect)
(letn ((parms (args 0))
(plen (length parms))
(vars (list-nth (sequence 0 (- plen 1) 2) parms))
(lists (list-nth (sequence 1 (- plen 1) 2) parms))
(body (1 (args))))
(comma-expand (map (fn ,vars ,@body) ,@lists))))
; where 'list-nth' is defined as:
(define (list-nth indices lisst)
(map (fn (n) (nth n lisst)) indices))
My definition of 'comma-expand' is:
Code: Select all
(define-macro (comma-expand form)
(catch
(cond ((quote? form)
(comma-expand-func (eval form) '()))
((list? form)
(eval (comma-expand-func form '())))
(true form))))
(define (comma-expand-func form acc)
(cond
((not (list? form)) form)
((empty? form) (reverse acc))
((lambda? form)
(let ((fn-tail (map (fn (x) x) form))) ; dirty trick.
(append (lambda) (comma-expand-func fn-tail '()))))
((quote? (form 0))
(comma-expand-func
(1 form)
(cons (append '(quote)
(list (comma-expand-func (eval (form 0)) '())))
acc)))
((list? (form 0))
(comma-expand-func (1 form)
(cons (comma-expand-func (form 0) '())
acc)))
((= ', (form 0))
(if (not (symbol? (form 1))) (throw 'CAN-ONLY-EXPAND-SYMBOLS))
(let ((sym-name (name (form 1))))
(if (= "@" (sym-name 0)) ; this means splice is required.
(letn ((var (symbol (1 sym-name)))
(val (eval var)))
(if (not (list? val)) (throw 'CAN-ONLY-SPLICE-LISTS))
(comma-expand-func (2 form) (append (reverse val)
acc)))
(comma-expand-func (2 form) (cons (eval (form 1)) acc)))))
(true
(comma-expand-func (1 form) (cons (form 0) acc)))))