| > Now if you were to do similar with something like an array, the original variable would be mutated. That is false. To do a similar thing with an array, we need a non-mutating operation which returns a new array which is like an existing array, but with an element prepended. Then we need a macro to mutate a place to replace an existing array in that place with a new such an array. Then, exactly the same kind of behavior will be reproduced: (defun array-cons (obj array)
(let ((new-array (make-array (list (1+ (length array))))))
(replace new-array array :start1 1)
(setf (aref new-array 0) obj)
new-array))
(defmacro apush (val array-var)
(assert (symbolp array-var) (array-var)
"fixme: simple implementation: ~a must be symbol")
`(setf ,array-var (array-cons ,val ,array-var)))
[1]> (defvar a #())
A
[2]> (defun x (v) (apush 'b v) v)
X
[3]> (x a)
#(B)
[4]> a
#()
What? Of course; we are not mutating any object here, but a variable: the local variable of x. [5]> (apush 1 a)
#(1)
[6]> (apush 2 a)
#(2 1)
[7]> (apush 3 a)
#(3 2 1)
Lists work this way because they are made of cells, and those cells are immutable (if you want them to be) for very good reasons. This is part of the essence of Lisp since the dawn of the language.It makes less sense to treat arrays that way. It's possible, but you need an exotic data structure to do it even halfway efficiently; that structure will never be as efficient as an ordinary mutable array for ordinary array work. Whereas, treating singly linked lists this way is almost free of additional cost. |
No, it's not. Notice how, in my example, the resultant list is updated in the function parameter, but not the initial var defined by defvar. Whereas if I made an array via (make-array), passed it into the function, and updated that by the way the language documentation tells you to (setf and one of the aref functions), you'd end up with both the function parameter and initial var both pointing to the updated value. These are two logically different behaviors! And that's exactly what my criticism stated: "When you pass a value to a function, is it by value or by reference? Who knows? Rules are non obvious, do not follow the principle of least surprise."
> Lists work this way because... It makes less sense to treat arrays that way.
Yes, that's exactly the point. The language is inconsistent and does not follow the principle of least surprise.