Arc Forumnew | comments | leaders | submitlogin
hmm, how about "#table(a 1 b 2)" for a literal table syntax?
4 points by aw 2903 days ago | 6 comments
akkartik's "Wart: File I/O for tables" ( reminded me of a question I've had for a while: what would be a good syntax for table literals?

MzScheme uses "#hash(...)" with dotted pairs:

  arc> (obj a 1 b 2)
  #hash((a . 1) (b . 2))
One possibility is that we could use Arc's terminology of "table" and drop the verboseness of the dotted pairs:

  arc> (obj a 1 b 2)
  #table((a 1) (b 2))
  arc> (#table((a 1) (b 2)) 'a)
In, I used a syntax of curly braces, with no internal parentheses grouping keys and values:

  arc> (obj a 1 b 2)
  {a 1 b 2}
  arc> ({a 1 b 2} 'a)
I had been worried that in tables with a lot of items, it would be easy to lose track of which ones were keys and which ones were values. But in practice I've found that my keys and values usually look different enough that it hasn't been a problem for me.

However, I've also noticed that I've never used the {...} table literal form myself when writing a program. I use it all the time when reading and writing table values in files, but when I'm creating a table value myself in a program, I'm always using something like (obj ...). In languages such as Perl or JavaScript, { ... } can be used as a template to construct table values:

  {"a": 5 - 4}
but in Lisp languages, programs and data have the same syntax, so with a literal syntax we'll get the literal expression, instead of the result of evaluating the expression:

  arc> {a (- 5 4)}
  {a (- 5 4)}
we could add some kind of "unquote" mechanism to the literal table syntax, but frankly when I want to put the result of an expression evaluation into a table, I just use (obj ...):

  (obj a (- 5 4))
Since I never type the literal table syntax, I think it's a bad idea to tie up valuable syntax real estate by using { ... }, so I'm back to using something like #table(...).

We could borrow the idea from (obj ...) that I used in my { ... } and dispense with the unnecessary internal parentheses grouping the keys and values. This would make #table(...) look like:

  arc> (obj a 1 b 2)
  #table(a 1 b 2)
  arc> (#table(a 1 b 2) 'a)
Thoughts? What do you think would be a good table literal syntax for Arc?

2 points by conanite 2903 days ago | link

Just to be clear: do you mean that an arc reader, on encountering

  #table(a 1 b 2)
should create an object equivalent to that returned by

  (obj a 1 b 2)

I see the difficulty with data vs code; I would expect a table literal in data to contain only other literals; but a table literal in code should also contain other code to be executed at runtime. At least, that's how table literals work in javascript, and it seems sensible. How would you go about advising the arc reader to differentiate between code and data? Or maybe the reader doesn't differentiate, but the compiler steps in when it encounters a table literal in code?

I use literal table syntax in javascript and ruby all the time, and wish I could in java ... so it would be great to have it in arc. And I prefer {...} to #table(...). What other uses might be better for "{..}" ? And couldn't we overload {} syntax anyway, if necessary?


2 points by aw 2903 days ago | link

> Just to be clear: do you mean that an arc reader, on encountering #table(a 1 b 2) should create an object equivalent to that returned by (obj a 1 b 2)

Yes, for that example. Though with #table(...) the values are always literals, so #table(a (+ 1 2)) wouldn't be (obj a (+ 1 2)) but instead would return the same value as (obj a '(+ 1 2))

a table literal in code should also contain other code to be executed at runtime ... I prefer {...} to #table(...)

right, if you want your table literal to be able to act like a "template" in the sense that you can insert expressions to be evaluated, then we'd need some kind of syntax to "unquote" the expression.

But I don't know how to do that and get everything to work.

I'm certainly open to suggestions, I'd just need working syntax to implement it.

The other option is not to try to get the syntax you like for constructing tables to be the same one as the writer/reader uses to output/read literal tables.

You could come up with a {...} syntax you like to use while programming, and that would be easier than coming up with a {...} syntax that the writer would also use to output tables, because that has the complication that every table needs to be able to be written out in a way that can be read back in.

For example, suppose you wanted a {...} syntax that worked like obj. Then, in regular Arc:

  arc> {a (+ 3 7)}
  #hash((a . 10))
or, using #table as the read/write syntax:

  arc> {a (+ 3 7)}
  #table(a 10)
That's easy to do. Now, if you also wanted your {...} to be used as the read/write syntax, that would be a lot more difficult, maybe impossible. At least I don't know how to design a syntax that would work for both, and still be able to read/write any table value.


1 point by rocketnia 2903 days ago | link

Just because there's a literal syntax for something doesn't mean it evaluates to itself; take lists and symbols, for example. :) How about a table syntax that evaluates like 'obj?

  arc> (= a 'foo)
  arc> {a (- 5 4)}
  {a 1}
  arc> '{a (- 5 4)}
  {a (- 5 4)}
  arc> `{,a (- 5 4)}
  {foo (- 5 4)}


1 point by aw 2903 days ago | link

With this syntax, how would we be able to use the same reader for reading data files and for loading Arc source code?

For example, what would

   (read "'{a (- 5 4)}")

Part of the power of Lisp is that Lisp programs can be manipulated as data and vice versa; this also makes coming up with new syntax hard.


1 point by rocketnia 2902 days ago | link

For that particular example, I see no problem:

  arc> (read "'{a (- 5 4)}")
  (quote {a (- 5 4)})
  arc> (eval:read "'{a (- 5 4)}")
  {a (- 5 4)}
  arc> (eval:eval:read "'{a (- 5 4)}")
  {a 1}
This doesn't strike me as being particularly odd, since it's exactly the same for lists already.

  arc> (read "'(a (- 5 4))")
  (quote (a (- 5 4)))
  arc> (eval:read "'(a (- 5 4))")
  (a (- 5 4))
  arc> (eval:eval:read "'(a (- 5 4))") ; would call a with 1
  Error: "reference to undefined identifier: _a"
Here's where I think the real gotcha is:

  arc> '{b 1 a 2 a 3}
  {a 3 b 1}
  arc> '`{,(uniq) 1 ,(uniq) 2}
  (quasiquote {(unquote (uniq)) 2})
  arc> `{,(uniq) 1 ,(uniq) 2}
  {gs1700 2}
Having a table as syntax can be a bit unintuitive, since it doesn't preserve order and multiplicity in the source the way a list does. Still, I personally try not to rely on side effects or evaluation order in expressions like this, and I figure that whenever someone does rely on those, they'll be writing code, so they can just use 'obj.

EDIT: Now that I think about it some more, those concerns can be mitigated too.

I do lots of my programming in Groovy, where the tables made by literal syntax do preserve order. They use LinkedHashMap, which essentially stores its references in both a hashtable and a linked list, so you get hashtable access speed and fast insertions and deletions while preserving order, at the cost of space efficiency.

As for the multiplicity issue, that can be caught by the reader. That is,

  arc> `{,(uniq) 1 ,(uniq) 2}
can produce an error at read time rather than silently treating the expression as `{,(uniq) 2}. Since any table already loaded has unique keys, nothing that can be written out will trigger this error when it's read back in.

EDIT: Well, I suppose there might be some question as to whether the reader should really have to compare things by 'iso, which would put a wrench in any plans for writing recursive/repetitive mutable structures. But I'll stop arguing with myself and give someone else a turn. :-p


3 points by aw 2901 days ago | link

oh, so for

  arc> {a (- 5 4)}
  {a 1}
is what is happening is that the reader parses "{...}" into a table object with a as a key and (- 5 4) as the value, and then the Arc compiler would, when it was passed a table object to compile, recursively compile the values of the table?

If so, then the part I wasn't getting was that the reader would return an actual table object which the Arc compiler would then work on... I hadn't thought of that approach.