[racket-dev] Square-bracket-sensitive macros in Scribble sandboxes

From: Eli Barzilay (eli at barzilay.org)
Date: Mon Nov 26 19:41:43 EST 2012

Two hours ago, Neil Toronto wrote:
> On 11/24/2012 05:36 PM, Eli Barzilay wrote:
> > I'm probably missing the problem, which wouldn't be surprising
> > since I didn't even tried to look up the `array' documentation...
> Well, it didn't exist until I pushed it on Saturday night, so
> looking it up wouldn't have done you any good. :D


> > 2. Alternatively, the last paragraph that you wrote above can also be
> >    easily translated by just replacing "[" with "#(", so the array
> >    [...]
> >
> > Am I missing something?
> Nope. This works really well, so thanks for the fantastical idea.
> I'm keeping it.

(OK, I'll assume that I'm on track then...)

> I considered having array data implicitly quoted, like in vector
> syntax.  With nested data, though, the number of design options
> grows quickly, the rules get complicated, and printing becomes a
> nightmare.
> For example, it's common to have arrays of Indexes, or (Vectorof
> Index), which would have to print like this with implicit quoting:
>    (array `#[,'#(0 0) ,'#(0 1)])
> Just working out the algorithm to print such a thing made me want to
> curl up and whimper.

Three sidenotes:

1. You probably mean (array `#[,`#(0 0) ,`#(0 1)]), right?

2. There's no real need for producing that yourself -- the racket
   printer will do the necessary work, and in this case since
   it's all just self-quoting values, then it will show it in a simple
   way: (array '#[#(0 0) #(0 1)]).  To see what I mean by "do the
   necessary work for you", just add
     (struct foo () #:transparent)
   and then use a few (foo)s in the vector syntax to see what I mean.

3. And besides, doing such algorithms is usually easier than it
   sounds.  (And IME, they're a perfect example where developement
   with test cases makes things way easier...)

So it sounds like there are two related issues: the syntax for code
that produces these things, and the printed syntax -- and they're
related because you want them to be the same.  If you choose to go
with the idiomatic thing where vectors (not your arrays) are
implicitly quoted, then things are relatively simple for you, but they
can be hard for users who want to write lots of expressions in them.
For example, I'll need to write something like

  (define (±1 i)
    (array `#(,(sub1 i) ,i ,(add1 i))))

make it return a 3x3 array, and you get some less convenient thing

  (define 1- sub1)
  (define 1+ add1)
  (define (±1 i j)
    (array `#(#(,(list (1- i) (1- j)) ,(list i (1- j)) ,(list (1+ i) (1- j)))
              #(,(list (1- i)     j ) ,(list i     j ) ,(list (1+ i)     j ))
              #(,(list (1- i) (1+ j)) ,(list i (1+ j)) ,(list (1+ i) (1+ j))))))

Your other choice is to make `array' a macro that implicitly
quasi-quotes all #(...) expressions and unquotes all other expressions
so you never need to unquote things but you do need to quote lists.
Maybe you can keep the simple thing simple, and for such cases where
there are lots of expressions that you need, have another variant as
this kind of macro?  Something like this:

  (define-syntax implicitly-unquoted
    (syntax-rules ()
      [(_ #(x ...)) `#(,(implicitly-unquoted x) ...)]
      [(_ x)        x]))

  (define-syntax-rule (array* xs) (array (implicitly-unquoted xs)))

  (define (±1 i j)
    (array* #(#((list (1- i) (1- j)) (list i (1- j)) (list (1+ i) (1- j)))
              #((list (1- i)     j ) (list i     j ) (list (1+ i)     j ))
              #((list (1- i) (1+ j)) (list i (1+ j)) (list (1+ i) (1+ j))))))


          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!

Posted on the dev mailing list.