Hacker News new | ask | show | jobs
by mtrower 1617 days ago
> When you pass a value to a function, is it by value or by reference? Who knows?

Value. Value, value, always value. However, it seems that maybe you don't understand exactly what the value is that you're passing.

> Notice how, in my example, the resultant list is updated in the function parameter

No [0]. It makes a new list (really a new cons cell, which contains the new item and then points to the old list [1]), and assigns that to v. And your example doesn't actually show this update happening, it just shows the return value of push (it so happens that it is updated, however). The original list --- passed in or otherwise --- is never changed. You say that you're "updating" a list, but you're not mutating or updating your data structure at all --- you're making something new, and assigning that to v.

> These are two logically different behaviors!

They are two logically different operations. Are you sure that you understand the data structures you're working with, and the operators that you're calling on them? Did you perhaps try to map concepts from another language into Common Lisp, find functionality that looked similar on the surface, then become surprised when the results were not identical?

Linked lists differ fundamentally in structure from arrays, and so the operators which are commonly used with them differ in turn. Perhaps you would like to compare arrays to vectors, as a closer approximation in data structures, with similar typical strategies of manipulation?

> does not follow the principle of least surprise.

You keep saying this, as if that is that. But nothing about your example is surprising to me, so I suppose this is a matter of perspective.

[0] http://clhs.lisp.se/Body/m_push.htm

[1] https://en.wikipedia.org/wiki/Linked_list

1 comments

> Value. Value, value, always value. However, it seems that maybe you don't understand exactly what the value is that you're passing.

This is a distinction without a difference.

> ...you're making something new, and assigning that to v.

Yes, I know that. The point is that the behavior seen by the user for similar operations is entirely different between lists and other pieces of Common Lisp.

> They are two logically different operations.

Yes, obviously. The point is that Common Lisp does an absolutely crap job of making these things actually consistent from the point of view of the user. The language is littered with entirely inconsistent behavior and choices.

> Common Lisp does an absolutely crap job of making these things actually consistent from the point of view of the user

Common Lisp provides a decently designed sequences abstraction which allows the encapsulated vectors and traditional unencapsulated lists, as well as strings, to be manipulated not just similarly, but by exactly the same code.

This was developed in recognition of the exactly the issue that you are getting at. Forty years ago, the group of people designing Common Lisp were aware of this desire to have a consistent access method for different kinds of sequences and they did something about it.

The charge of "absolutely crap job" can only be fairly leveled at a language that make no effort to provide for uniform treatment of encapsulated vectors and unencapsulated lists.

What looks "surprising" depends on your background. I agree that Lisp contains surprises for someone who has programming experience, but that experience is limited to Python or Javascript which provide only encapsulated arrays as the principal sequence aggregation mechanism.

I had two decades of programming experience coming to Lisp, and had written C programs which used both unencapsulated lists:

   list_node *list = NULL;
and I had written code with encapsulated ones:

   list_block list = LIST_EMPTY_INITIALIZER(list); // eg circular, expanding to { &list, &list }
leveraging the advantages of both.

So, I wasn't surprised in any way. From the description of NIL and the cons cell linkage in the book I was reading, I instantly recognized it instantly as the unencapsulated style of lists, like "list_node *list = NULL".

In C, it would be obvious that this can't work:

   list_node *list = NULL;
   list_append(list, list_node_new(42));

   // wrongly expecting list to have changed from NULL to non-null!
but that, with an encapsulating list block object instead of a raw pointer to a node, this could work:

   list_block *list = list_new();
   list_append(list, list_node_new(42));
(Because C does not provide either of these, you can't blame the language for misunderstanding anything: only yourself, if you wrote that list yourself, or the library author.)

I remember that was intriguing to me how Lisp is getting away with the unencapsulated single linkage for everything: like how that is the list structure for the entire language. In the light of the functional programming (you can always just keep consing up new conses to transform lists) together with the garbage collection, it very soon clicked for me. I remember thinking that if we could just keep mallocing new nodes and not worry about freeing, that would be pretty nice to work with in C.