So I've been writing a JIT runtime for a Lisp dialect [0] for a while and got into the habit of feeling jealous of other languages with array primitives, especially after I learned what you can do with arrays or opaque lists (see V8 element kinds [1] for an example). And then I discovered this article (or complaint?) by Xah Lee.
Aesthetically speaking, I quite like conses, in that it is powerful and flexible enough to hold both trees and lists. But performance-wise, I guess it's always the simplest things that are hard to optimize, and cons-style linked lists can't really compete with arrays [2], if you are aiming to get on par with V8.
However, I still believe that, with a JIT runtime, it is possible to have some Lisp lists backed by arrays and am working on it [3]. Currently, =setcdr= causes most trouble for me and I don't know if the additional checks are going to ruin the performance improvement brought by arrays. But if things go well, we might get to see what conses could have cost us.
If you want vectors, use vectors, elisp has them too as primitives. (I don't mean to suggest you don't know that, but still, you can just use vectors.)
I was writing from the aspect of an Lisp implementer, so that means it is not how I can just use vectors but about how existing code represents a sequence, and it is part of the job of the runtime to make it as fast as possible.
From what I see from existing ELisp code (at least in the Emacs codebase), the idiomatic representation of sequences (fixed-size or not) is using cons lists. And it is not surprising: Emacs vectors are fixed-size and that makes it very inflexible and only suitable for a few things. This matters because, for example, if you want Emacs to compete with VSCode in performance, you eventually compares how idiomatic code performs. Note that how cons lists affect performance in real-world ELisp code remains unknown because it is yet to be benchmarked, but exposing the internals of idiomatic lists as conses does pose some challenges for an implementer aiming for further optimizations.
Emacs Lisp vectors being fixed seems like an easily fixable problem. More functions can be defined to do useful things with vectors including mutations. If it is important to keep the existing type produced by make-vector immutable, a separate mutable variant can be introduced. The mutating functions blow up if applied to the immutable type.
Aesthetically speaking, I quite like conses, in that it is powerful and flexible enough to hold both trees and lists. But performance-wise, I guess it's always the simplest things that are hard to optimize, and cons-style linked lists can't really compete with arrays [2], if you are aiming to get on par with V8.
However, I still believe that, with a JIT runtime, it is possible to have some Lisp lists backed by arrays and am working on it [3]. Currently, =setcdr= causes most trouble for me and I don't know if the additional checks are going to ruin the performance improvement brought by arrays. But if things go well, we might get to see what conses could have cost us.
[0] Emacs Lisp, in Java, with Graal Truffle
[1] https://v8.dev/blog/elements-kinds
[2] https://en.wikichip.org/wiki/pointer_chasing
[3] I am not aware of other Lisps doing this. Cdr-coding is the closest, but it is still a linked list (implicitly).