Skip to content

Comment on Generalized Macros

Comments

Regular macros can give you this in a disciplined way:

You make a macro called crazy-macrolet which is used like this:

  (crazy-macrolet ((crazy-macro (left-forms right-forms arg ...)
                     ....)
                   (other-crazy-macro (...)))
    body)
Inside body, you use the local crazy macros. A little language made of these crazy macros can be wrapped up in a big macro.
  (defmacro bobs-crazy-dsl (&rest forms)
    `(crazy-macrolet ((bobs-crazy-macro ...))
        ,@forms))
Then, you can only use bobs-crazy-macro in code wrapped in (bobs-crazy-dsl ...).

crazy-macrolet needs to implement a code walker to in order to expand those macros.

Macros can be context-dependent without having access to the literal forms to the left or right (or elsewhere).

In TXR Lisp, I implemented tagbody as a macro, providing a measure of CL compatibility. The go operators are local macros. They do not expand in a context-free way; they communicate with the surrounding tagbody.

So for instance if go is asked to jump to a nonexistent label, it errors:

  1> (expand '(tagbody (go a) b))
  ** go: no a label visible

  1> (expand '(tagbody (go a) a))
  (let ((#:tb-id-0019
         (gensym "tb-dyn-id-"))
        (#:next-0020
         0))
    (sys:for-op ()
      (#:next-0020)
      ((sys:setq #:next-0020
         (block* #:tb-id-0019
           (sys:switch #:next-0020
             #(((return* #:tb-id-0019
                         1))
               ()))
           ())))))
So obviously, (go a) is behaving differently based on whether it can "see" that there is an a in its context, on the left or right side.
AboutSource Built by g1lg1l

Hackerly is an independent reader for Hacker News, built on the public HN API. Not affiliated with Y Combinator.