Arc Forumnew | comments | leaders | submitlogin
Question of common cases: dotted lists, unquote-splicing and rest parameters
2 points by evanrmurphy 4829 days ago | 14 comments
When do you most often use dotted lists and unquote-splicing in your code?

---

I'll start. The only time I find myself writing dotted lists is in the parameter lists of functions and macros, where I use them all the time to name rest parameters:

  (def foo (x . args) blah)
As for unquote-splicing, most often I use it to splice rest parameters into macro definitions:

  (mac bar body 
    `(blah ,@body))
I'm not sure this is the only case I have for using unquote-splicing, but it is by far my most common one.


2 points by waterhouse 4829 days ago | link

I think there are only two conceivable, allowed-by-the-language cases where you could have dotted lists in code: (a) literal dotted lists, as in '(1 . 2), and (b) parameter lists. [Edit: Oh man, I forgot about destructuring, as rocketnia points out. I do sometimes destructure with dots. They are instances of parameter lists, though, and can be handled as such.]

(b) can be easily handled in other ways (e.g. in CL, you say "&rest args" instead of ". args").

(a) is extremely rare, and it's never necessary in code--you could replace it with (cons 1 2). There is the fact that, inside the body of a function, (cons 1 2) will create a new cons cell every time the function is called, whereas '(1 . 2) will always refer to the same cons cell[1]; however, you could fix this by wrapping it in (let u (cons 1 2) ...) and referring to u instead of '(1 . 2).

The only potential problem I see (with dropping dotted lists) is that you might need to read (or print) dotted s-expressions as data. That does come up now and then; you might represent some compound data structure (like a rational number) with a cons cell, which is slightly more efficient than using a two-element list (two cons cells) and perhaps nicer. Also, Racket prints hash tables with dots--I'm sure you routinely see things like #hash((a . 1) (b . 2)). And someone might create some kind of tree structure with conses.

You could dotted lists for now, then, when you find them necessary, create a private notation for non-proper-list trees (say, using ^ in place of . in trees). Then there would be no problem except dealing with tree output from other Lisp implementations, which is rare enough that you can probably afford to ignore it for now, and if it comes up, just write something to translate back and forth. That would be my advice.

As for unquote-splicing: Most of the time I use it in macro definitions, most of the time for "body" arguments. Here is a handy terminal command to illustrate:

  $ egrep -o ',@[^ )]+' a | sort | uniq -c
     2 ,@(apply
     1 ,@(explode
     1 ,@(flat1
     2 ,@(if
     5 ,@(map
     1 ,@(mappend
     5 ,@args
     1 ,@binds
    16 ,@body
     1 ,@indices
     1 ,@leads.expr
     2 ,@vars
If what you're wondering is whether I use unquote-splicing to make dotted lists, the answer is no, and I would consider it bad practice (if you're making something as unusual as that, you should use cons or something).

[1] For example:

  arc> (def meh (x) '(1 . 2))
  #<procedure: meh>
  arc> (meh 2)
  (1 . 2)
  arc> (= (car (meh 2)) 5)
  5
  arc> (meh 3)
  (5 . 2)
  arc> (def uch (x) (cons 1 2))
  #<procedure: uch>
  arc> (= (car (uch 2)) 5)
  5
  arc> (uch 3)
  (1 . 2)

-----

4 points by bogomipz 4828 days ago | link

Ever since I first started learning the fundamentals of Lisps, I always wondered why dot notation couldn't be used interchangibly with 'apply, eg:

  (def foo (x . xs)
    (bar x . xs))
instead of:

  (def foo (x . xs)
    (apply bar x xs))
The first snippet above seems much more intuitive to me than the second.

-----

5 points by aw 4828 days ago | link

Assuming you don't change the syntax of dotted lists, a problem is that you wouldn't be able to use an expression for "xs".

The dot notation creates a cons for you, and a cons creates a regular list if its second argument is nil or another cons (which itself is a regular list).

Thus

  '(a . (b))
works like

  (cons 'a '(b))
which in turn is like

  '(a b)
which you can see for yourself:

  arc> '(a . (b))
  (a b)
So an expression like

  (bar x . (foo))
that we'd want to work like

  (apply bar x (foo))
is parsed by the reader like this:

  arc> '(bar x . (foo))
  (bar x foo)
and by the time the compiler sees the expression there's nothing there to tell it that a dot was used.

Of course, if you change the reader so that a dot doesn't produce a dotted list but instead creates a token for the compiler to see, then you can do whatever you want... though you'd also need to extend the compiler to handle the dot notation in the other places which is currently handled by the reader.

-----

2 points by rocketnia 4827 days ago | link

Great point.

For what it's worth, 'apply could be moved to arc.arc:

  (def apply (self . args)
    (zap rev args)
    (zap [rev:+ (rev:car _) cdr._] args)
    (self . args))
I think the number of times I use 'apply with a variable in the final position is enough for the syntax to be relevant to me... but then I'd be likely to refactor (a b c . d) into (apply a b c (something-else)) and vice versa all the time, which would be a bit of a pain. Then again, it's balanced against the cost of refactoring (a b c) into (a b c . d) and vice versa....

by the time the compiler sees the expression there's nothing there to tell it that a dot was used.

In Racket's syntax there is a way, I think. It makes syntax more complicated than just its conses, though. You'd have to use an Arc variant of syntax-quasiquote (or just syntax-quasiquote) to construct it in macros.

Meanwhile, the (cdr `(foo . ,<scheme-expr>)) quirk would become more of a bug than it already is. But then I assume whatever Arc hack implements this syntax would also have '$ built in, so there's probably no point to keeping the quirk around.

-----

1 point by evanrmurphy 4828 days ago | link

I like that idea.

I don't see a way you could use it if xs were the only argument, i.e. a way to rewrite `(apply bar xs)`, but in that case apply is more paletable anyway:

  (def foo xs
    (apply bar xs))

-----

3 points by rocketnia 4827 days ago | link

  (def foo xs
    (bar . xs))

-----

1 point by evanrmurphy 4827 days ago | link

I see, so `'(bar . xs)` is a cons cell and `(bar . xs)` is equivalent to `(apply bar xs)`.

-----

1 point by rocketnia 4828 days ago | link

I've wondered that too. XD

-----

2 points by rocketnia 4829 days ago | link

I use them for those cases and for destructuring.

  (iflet (first . rest) args
    ...)
This is practically the same as the rest arg case, and whatever workaround you're considering for rest args will probably work just as well here.

Speaking of which, why not just use another character for dotted lists, like (a b c & d)?

EDIT: Oh, right, you just mentioned that possibility in http://arclanguage.org/item?id=13248.

-----

2 points by evanrmurphy 4828 days ago | link

I noticed recently that Racket's dotted infix notation (unrelated to dotted lists) [1] spills over into arc:

  arc> (1 . < . 2)
  t
  arc> '(1 . < . 2)
  (< 1 2)
So, `(x . y . z)` expands to `(y x z)`, for any x, y, z. Didn't know we had this feature.

---

[1] http://docs.racket-lang.org/guide/Pairs__Lists__and_Racket_S...

-----

1 point by evanrmurphy 4829 days ago | link

The reason I care about this is I think I've devised a way to allow javascript-style infix dotting in my lisp->js project [1], even between non-symbol expressions such as

  ($ "#foo").(addClass "bar").(show "slow")
that arc's ssyntax system doesn't usually touch. The problem is my solution would break:

1) unquote-splicing if you're not just using it to splice rest parameters

2) dotted lists (and, in turn, rest parameters), unless I pick another symbol for denoting them, e.g. `dot` or `..`

---

[1] https://github.com/evanrmurphy/SweetScript

-----

2 points by shader 4828 days ago | link

What we really need is our own reader for arc, so that ssyntax isn't limited to intra-symbol, but can be used between other tokens as well, such as lists and strings.

-----

1 point by evanrmurphy 4828 days ago | link

Can you help me understand why Racket's reader couldn't be extended to do this? It seems very extensible [1]. I've already gotten good mileage out of hacking it with aw's extend-readtable [2]. (Come to think of it, I should continue playing around with this to try and implement the JavaScript-style dot operator.)

I also wonder if rntz's arcc might be useful here [3]. (I haven't tried it yet.)

---

[1] http://docs.racket-lang.org/guide/hash-reader.html

[2] http://awwx.ws/extend-readtable0

[3] http://arclanguage.org/item?id=11128

-----

2 points by aw 4828 days ago | link

The reader is implemented in Arc in the runtime project I'm working on (https://github.com/awwx/ar).

-----