guile-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

A convention for syntax-rules pattern variables


From: Taylan Ulrich Bayırlı/Kammer
Subject: A convention for syntax-rules pattern variables
Date: Thu, 03 Sep 2015 00:17:25 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux)

I'd like to endorse a new(?) convention for how to name pattern
variables in syntax-rules macros.

Over time, I've found myself suffering from two problems when working
with syntax-rules.  Maybe you can sympathize.

1. When I lose concentration, I easily end up writing code that tries to
   let-bind a pattern variable as if the let were going to shadow it:

    (define-syntax frobnicate
      (syntax-rules ()
        ((_ foo bar)
         (let ((qux (quxicate foo))
               (fux (fuxicate bar)))
           (do-something)
           (start-losing-concentration)
           (let ((foo (dequxicate qux)))  ;BEEEP!
             ...)))))

If the macro is passed something other than an identifier for the 'foo'
slot, then that will result in a somewhat cryptic error message about
having used 'let' wrong, which is pretty irritating because how can one
possibly use something as simple as 'let' wrong?

There's also a more general point to be made there about the abnormal
status of pattern variables within code.  Normally, since Scheme is
hygienically lexically scoped, you can let-bind *anything*, but pattern
variables break that.  Syntax-rules templates are very special in that
they contain these strange identifiers that behave unlike everything
else in Scheme...

2. When I want to let-bind a pattern variable's expansion immediately to
   avoid multiple evaluations of the expression, I fumble around to
   somehow find a suitable name for both the pattern variable and the
   let-bound variable.  Maybe the cleanest you can do is suffix it with
   "-expr":

    (syntax-rules ()
      ((_ name-expr ...)
       (let ((name name-expr))
         ...)))

That gets pretty annoying especially because it's out of the question to
use it consistently for all pattern variables that denote an expression;
the name of the pattern variable pretends to have that suffix for
semantic merit but then other variables with the same semantics lack the
suffix, which I find kind of irritating.  If I name a variable foo-expr
because it's an expression, then I should name all expression-holding
variables that way.

The other alternative is to name both variables such that they are just
alternative names for the same concept.  E.g. one could be slightly more
verbose, 'foo-name', indicating what sort of name, and the other could
be the shorter 'name'.  Also annoying because there's not even any logic
behind it now; I just use one more verbose and one less verbose
variation for the same thing, side by side, just because I have to give
the same thing two different names...


Now as for the solution, it's really simple.  Just put pattern variables
in angle brackets, like BNF nonterminals:

    (define-syntax frobnicate
      (syntax-rules ()
        ((_ <foo> <bar>)
         (let ((qux (quxicate <foo>))
               (fux (fuxicate <bar>)))
           (do-something)
           (start-losing-concentration)
           (let ((foo (dequxicate qux))) ;can't possibly reuse <foo> by accident
             ...)))))

    (syntax-rules ()
      ((_ <name> ...)
       (let ((name <name>))  ;it's the same name, yet a different name!
         ...)))

I find this *very* visually appealing because <foo> is already common
notation for a "placeholder," imitating BNF.  In fact, more than just
being visually appealing, it makes my mind immediately pick up that the
template of the syntax-rules is really a template, with <subforms> that
could be *any* grammatical construct getting inserted into certain
positions.

When you use the (<foo> <foo>* ...) idiom in a pattern to allow
one-or-more instances of something, it again looks exactly like BNF, so
much that a foreign programmer looking at the code might just
immediately figure out that there's pattern-matching going on there.


I can't imagine anyone confusing this notation with record type names,
because they usually occur in such different contexts.

If you define a record type in a macro, then yes, it might be mildly
irritating that the <foo> in '(define-record-type <foo> ...)' could be
the literal name for the record type or it could be a pattern variable,
but I think this is a rare enough situation to make it a non-issue.
Specifically, it's unlikely to lead to an actual bug, because the whole
<foo> notation draws your attention and raises your awareness.  So in
terms of bugs prevented, it's a clear win; in terms of irritations
saved, it's still a big net positive.  And in the absolute worst case
you can locally deviate from the convention, saving the <foo> notation
for your use of 'define-record-type'.


I've been using this convention for a while now with great pleasure, and
will keep doing so in the foreseeable future.  I hope others like it
too; I'd love it to become widespread.


Happy Hacking,
Taylan



reply via email to

[Prev in Thread] Current Thread [Next in Thread]