macrosschemeassociative-arraychez-scheme

(Chez) Scheme macro for hiding lambdas


I would like to write a macro to create shorthand syntax for hiding more verbose lambda expressions, but I'm struggling to understand how to write macros (which I realize is an argument against using them).

Given this example:

(define alist-example
  '((x 1 2 3) (y 4 5 6) (z 7 8 9)))

(define ($ alist name)
  (cdr (assoc name alist)))

((lambda (a) (map (lambda (x y z) (+ x y z)) ($ a 'x) ($ a 'y) ($ a 'z))) alist-example)
((lambda (a) (map (lambda (y) (/ y (apply max ($ a 'y)))) ($ a 'y))) alist-example)

I would like to write a macro, with-alist, that would allow me to write the last two expressions similar to this:

(with-alist alist-example (+ x y z))
(with-alist alist-example (/ y (apply max y)))

Any advice or suggestions?


Solution

  • Here is a syntax-rules solution based on the feedback that I received in the other answer and comments:

    (define ($ alist name)
      (cdr (assoc name alist)))
    
    (define-syntax with-alist
      (syntax-rules ()
        [(_ alist names expr)
         (let ([alist-local alist])
           (apply map (lambda names expr)
                  (map (lambda (name) ($ alist-local name)) (quote names))))]))
    

    Here is some example usage:

    > (define alist-example
      '((x 1 2 3) (y 4 5 6) (z 7 8 9)))
    > (with-alist alist-example (x) (+ x 2))
    (3 4 5)
    > (with-alist alist-example (x y) (+ x y))
    (5 7 9)
    > (with-alist alist-example (x y z) (+ x y z))          
    (12 15 18)
    

    This answer stops short of solving the more complicated example, (with-alist alist-example (/ y (apply max y))), in my question, but I think this is a reasonable approach for my purposes:

    > (with-alist alist-example (y) (/ y (apply max ($ alist-example 'y))))
    (2/3 5/6 1)
    

    EDIT: After some additional tinkering, I arrived at a slightly different solution that I think will provide more flexibility.

    My new macro, npl, expands shorthand expressions into a list of names and procedures.

    (define-syntax npl
      (syntax-rules ()
        [(_ (names expr) ...)
         (list 
          (list (quote names) ...)
          (list (lambda names expr) ...))]))
    

    The output of this macro is passed to a regular procedure, with-list-map, that contains most the core functionality in the with-alist macro above.

    (define (with-alist-map alist names-proc-list)
      (let ([names-list (car names-proc-list)]
            [proc-list (cadr names-proc-list)])
        (map (lambda (names proc)
               (apply map proc
                      (map (lambda (name) ($ alist name)) names)))
             names-list proc-list)))
    

    The 3 examples of with-alist usage above can be captured in a single call to with-alist-map.

    > (with-alist-map alist-example
                    (npl ((x) (+ x 2))
                         ((x y) (+ x y))
                         ((x y z) (+ x y z))))
    ((3 4 5) (5 7 9) (12 15 18))