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

From: Neil Toronto (neil.toronto at gmail.com)
Date: Thu Nov 29 16:59:51 EST 2012

I've pretty much made up my mind on this, so please don't feel like you 
have to take time to respond point-by-point. Unless you've seen a gaping 
hole in my reasoning, anyway, then by all means, have at it.

On 11/26/2012 05:41 PM, Eli Barzilay wrote:
> Two hours ago, Neil Toronto wrote:
>> 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?

The inner quasiquotes can be quotes because #(0 0) and #(0 1) are 
vectors, not array rows. If they were array rows, they still shouldn't 
need to be quasiquoted. Well, depending on design...

Array rows don't exist as separate objects (an array is just a 
function), so I have a hard time intuiting what it means to quote the 
inner ones. But here are some (just some) design options:

  1. Allow a quote just on the outer "#[...]" and have it apply to all
     nested "#[...]".

  2. Allow a quote on the outer "#[...]" and have it apply to all
     nested "#[...]", but allow them to override with their own quotes.

  3. Allow quotes at any level, unpropagated.

  4. Avoid difficulty altogether by having array syntax not quote
     its elements.

#1 might be easy. #2 might be cool, but complicated. #3 is kind of 
weird, and I think it means the only quotes that matter are the 
innermost. For all of these, there are additional design options. For 
example, for #3, should quasiquoting outer rows be necessary if you want 
to quasiquote inner ones? For #2, how would one spell "override" in a 
way that distinguishes it from "element"?

#4 avoids all these choices, and that's what is currently implemented.

> 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...)

I'm okay with inventing apparently complicated recursive algorithms that 
eventually come down to a few well-chosen rules. (That's language 
semantics in a nutshell.) My problem has been *coming up with the test 
cases* for the various options I listed above.

Now, I know where your brain just went, so let me head it off. You were 
about to reply with a bunch of starter test cases or a load of helpful 
hints. (Because that's who you are... and I appreciate it!) Let me make 
a case for not developing another set of array parsing/printing 
algorithms first, starting with your example:

> 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
> like
>
>    (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))))))

I wouldn't want to write that, either. Right now, you'd write

   (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))]])

The vector syntax just delimits rows; it never quotes. Square parens 
aren't required, but make rows easier to distinguish. Arrays' custom 
printers print square parens, which gives feedback when a user is 
confused about whether #(...) means a row or a vector-valued element.

Quotes and quasiquotes stop the `array' macro's recursion into rows, so 
quotes always mean "this is an element". For example, this is a 
zero-dimensional array that contains a vector:

   (array '#(0 1 2 3))

Here are my reasons for leaving it as it is:

  * Most array elements will be self-quoting anyway (numbers, strings,
    other primitive types).

  * It's easy enough to remember the exception "quote literal vectors".
    Also, Racket's printer writes an explicit quote before literal
    vectors, so arrays of array indexes parse and print the same.

  * I'm having a hard time with test cases for implicitly quoted array
    rows, which means math/array users would probably have a hard time
    reasoning about it as well.

  * There are enough possible design choices that give rise to similar-
    but-subtly different behavior that it would be easy for users to
    get confused about the actual rules.

At any rate, I'm all for simple. Thanks for hashing this out with me.

Neil ⊥


Posted on the dev mailing list.