Here's a new arc-level implementation of len, that works for dotted lists as well, using the mz/ac-tunnel patch:
(def len (x (o c 0))
(if (isa x 'string) (mz:string-length x)
(isa x 'vec) (mz:vector-length x)
(isa x 'table) (mz:hash-table-count x)
(and atom.x x) (+ c 1)
acons.x (len cdr.x (+ c 1))
c))
I think the reason that lists are usually flat lists is that a flat list is homogenous -- each car is data, and each cdr is the next element, or nil. Dotted lists add another case which you'd have to handle. It's just simpler to deal with flat lists.
If you're just dealing with two- or three-element lists, then it might pay off to use dotted lists. In such short lists, you'd be getting a space saving of 33% or 25% per list.
Arc uses vectors to implement tagged procedures. Type the name of a macro in on the repl, and it displays a vector:
arc> def
#3(tagged mac #<procedure:.../arc/arc.arc.scm:151:11>)
That's a vector with 3 elements: tagged, mac, and a procedure.
Arc doesn't currently let you construct or manipulate your own vectors, but they could be used to provide transparent meta-data attached to functions and variables, such as where they were defined, and what the source code was that did so.