Arc Forumnew | comments | leaders | submitlogin
Handling dotted lists
9 points by dido 6109 days ago | 4 comments
There seems to be a dearth of functions that can handle dotted lists correctly within the main Arc source, apparently there is only the dotted predicate and makeproper that converts a dotted list into an ordinary one. In particular, join and map die with a cryptic error message if they are used with dotted lists. I was hoping, in particular, that join would have the semantics of Common Lisp append, but unfortunately that doesn't seem to be the case:

  arc> (join '(a b c) '(d e . f))
  Error: "Can't take car of f"
Neither does map it seems.

  arc> (map idfn '(a b . c))
  Error: "Can't take car of c"
I can't think of any logical reason why these sorts of functions should be restricted to operating only on proper lists. I also notice that there doesn't seem to be an equivalent of the Common Lisp list* function, which makes the last cons of the constructed list dotted.

I have had to write my own versions, based on the Common Lisp append from the SBCL source:

  (def join* args
    (with (jinto
	   (afn (last-cons current rest)
	        (if (acons current)
		    (self (= (cdr last-cons) (list (car current)))
  			 (cdr current) rest)
	  	    (~no current)
		    (err "current expected to be a list")
		    (no (cdr rest)) (= (cdr last-cons) (car rest))
		    (self last-cons (car rest) (cdr rest)))))
    ((afn (lists)
	  (with (cur (car lists) rest (cdr lists))
	    (if (no rest) cur
		(acons cur)
		(let result (list (car cur))
		  (do
		    (jinto result (cdr cur) rest)
		    result))
		(no cur) (self rest)
		(err "cur is not a list")))) args)))
and map:

  (def map* (f xs)
    (if (atom xs)
        (f xs)
        (cons (f (car xs)) (map* f (cdr xs)))))
I think that the standard functions ought to behave at least as reasonably as their Common Lisp equivalents when confronted by dotted lists, as these sorts of lists do appear from time to time.


1 point by Jesin 6109 days ago | link

I haven't tested this and it's not very good anyway, but here's this:

  (def list* args
    (aif (cddr args) (cons (car args) (apply list* it))
         (cdr args) (cons (car args) it)
         (car args)))

-----

1 point by eds 6109 days ago | link

Just wondering what (~no current) does in join* . Can't you just use current as a boolean value?

-----

1 point by dido 6109 days ago | link

It's supposed to be a predicate for a non-nil atom. I just realize that could better be written as (~acons current) instead, given that (acons nil) is also nil.

-----

2 points by eds 6108 days ago | link

I was confused because (~no current) is just the same as current by itself, and I didn't realize you wanted to test for non-nil atoms. Obviously (~no current) isn't what you want, because it returns true for lists.

  arc> (~no nil)
  nil
  arc> (~no 'a)
  t
  arc> (~no '(1 2 3))
  t
But I don't think (~acons current) does what you want either. Because (acons nil) is nil, (~acons nil) is true.

  arc> (~acons nil)
  t
  arc> (~acons 'a)
  t
  arc> (~acons '(1 2 3))
  nil
I think (~alist current) returns true for non-nil atoms, because (alist nil) is true.

  arc> (~alist nil)
  nil
  arc> (~alist 'a)
  t
  arc> (~alist '(1 2 3))
  nil

-----