"Is a cons based list system actually bad? Are there better things that we could be doing?"

They are interesting questions. I think that to be a lisp, a language must homoiconic and applicative. A cons-based system is non-essential, and I am coming to agree with the OP that it's not the best way.

Update: Hmm... my mind is playing tricks on me now. What would you use besides conses to represent s-expressions? Maybe that is a good role for cons.

I wonder if there's a way to merge quote and lambda into a single operator. This is done in concatenative languages like Joy and Factor, where a "quotation" acts both as quoted code and as an anonymous function. But I'm struggling to see how you could translate that into applicative terms.

What are s-expressions? I thought their main aspect was the way they used lists to represent user-extensible syntax forms (macro forms, function applications, fexpr applications). I'm not a fan, but even if we assume s-expressions, they could always use some other implementation of lists.

--

"I wonder if there's a way to merge quote and lambda into a single operator."

Do you mean like in PicoLisp, where all user-defined functions are lists containing their code, and applying a list causes it to be interpreted as a function?

I don't like it, 'cause you don't get any lexical scoping this way. It probably makes more sense in Factor and Joy thanks to the lack of local variables for run-time-created functions to capture.

"What would you use besides conses to represent s-expressions? Maybe that is a good role for cons."

Arrays, objects, pretty much anything that can represent nested sequences. In fact, in PyArc, conses are a class, because Python has an infatuation with classes.

I don't see conses as an "ultimate abstraction". They're a low-level primitive that can be used to create lists, among other things. To put it bluntly, they're a simple way to implement linked lists. The only difference between an array and a linked list is performance, but they have the same semantics.

Most popular languages choose arrays as their default sequence type, but Lisp chose linked lists. I could represent conses as Python lists (which are like arrays), and then define car so it returns list[0] and cdr so it returns list[1:].

As far as Arc programs would be concerned, everything would look exactly the same. The only difference is that certain operations (like prepending) would be slower with arrays than with a linked list. Specifically, (cons 'a some-list) would be O(n) rather than O(1).

So... to say that conses are bad is like saying that arrays are bad. They're both sequence types, that have differing performance characteristics. Use whichever one happens to suit your language/program the best.