Arc-macros, baby. Arc-macros. (part II)

More useless reader macros ahead. This time, let's implement Arc's [ ... _ ... ] lambda shortcut. In Chicken Scheme, of course. ^_^

I will be using { } rather than [ ]. Incidentally, Chicken already supports { } as an alternative for ( ):

> '{1 2 3}
1 2 3)
> {+ 1 2}
3

This reader macro will override that. Anyway, the code follows. It's clumsy (although not as clumsy as my first version), and there's probably a better/shorter way to do it, that I'm not aware of yet. In any case, it's naive; for example, this version does not allow nesting the { } constructs. Also, the replace-underscore function won't replace underscores in nested lists. 1) And so on. Remember that this is just for demonstration purposes before pointing out the zillions of flaws. :-)

(define (read-until-next-curly port)
  "clumsy way to read up until the first }."
  (read-token (lambda (c) (not (char=? c #\}))) port))

(define (replace-underscore exprlist)
  (define (replace-underscore-item y)
    (if (equal? y '_) 'x y))
  (map replace-underscore-item exprlist))

(define (make-lambda-form exprlist)
  `(lambda (x) (,@exprlist)))

(define (eval-string s)
  (with-input-from-string s read))

(define (transform-into-lambda s)
  (let* ((expr (eval-string s))
         (expr2 (replace-underscore expr)))
    (make-lambda-form expr2)))

(set-read-syntax! #\{
  (lambda (port)
    (let* ((s (read-until-next-curly port))
           (t (string-append "(" s ")")))
      (read-byte port) ; read trailing '}'
      (transform-into-lambda t))))

Testing...

(print { + _ 1 })     ; => #<procedure (? x)
(print ({+ _ 1} 44))  ; => 45

(print (map { * _ 2 } '(1 2 3 4 5 6)))
; => (2 4 6 8 10 12)

Cool. Of course, if you use a lot of small lambdas, then it's possible to write a macro that shortens things for you, rather than having to resort to syntax changes. For example:

> (define-macro (fn . body)
>   `(lambda (_) ,body))
> (fn + _ 1)
#<procedure (? _)>
> ((fn + _ 1) 44)
45

;; compare:
> ((lambda (x) (+ x 1)) 44)
45

Anyway, much like the previous post, this was just an excuse to explore reader macros a bit more. So don't shoot me...

1) On a side note, why doesn't SRFI-1 have a function to replace items in a list?

3 Comments »

  1. John Cowan said,

    January 31, 2008 @ 5:59 pm

    Because it's a one-liner:

    (define (set-elt! lst n obj) (set-car! (drop lst n) obj)

  2. Hans Nowak said,

    January 31, 2008 @ 6:35 pm

    Well, I was talking about replacing all instances of X with Y, either shallow or nested. Note that I am not asking you to write the function for me! :-) I was just wondering why such a beast does not exist. Maybe it's not common enough an operation? Or is this another one-liner? :)

  3. Kon Lovett said,

    February 15, 2008 @ 9:55 pm

    If your domain is not boolean than take a look at "filter-map".

    Chicken uses the "cut" macro for '{foo _ 2 _}' - '(cut foo <> 2 <>)'. Not as concise but save thes '{}' brackets for something really important.

RSS feed for comments on this post · TrackBack URI

Leave a Comment