adamsol / Pyxell

Multi-paradigm programming language compiled to C++, written in Python.
MIT License
54 stars 6 forks source link

Tuples are mutable #1

Closed skaller closed 3 years ago

skaller commented 3 years ago

The docs say "Tuples are mutable, but they have value semantics, so they are hashable and can be passed around as if they were immutable."

No. Variables may be mutable, including variables of a tuple type. Modifying a component of a variable of a tuple type is equivalent to constructing a new tuple of the same type from the old one with a different value for that component, and assigning it. I think this is what you meant, it just reads wrong.

adamsol commented 3 years ago

Pyxell uses C++'s std::tuple internally and it works like I described. The effect would be probably the same if it worked like you described it, but it would be a different implementation. Maybe faster, though I'm not sure, because tuples would then require memory management just like containers.

skaller commented 3 years ago

How do you modify a tuple that is not stored in a variable?

skaller commented 3 years ago

Sorry , let me clarify. Value semantics is necessarily purely functional and cannot include any notion of mutation. This is true by definition. I'm not trying to tell you what your language does or should do, just pointing out an error in the documentation. Value semantics means roughly "it works like an integer", values are always intrinsically immutable. Operations such as C++ increment (eg x++) do not operate on values, but on objects. An object is a location in which values can be stored, so mutation could be allowed, by modifying what is stored at that location. Pointers are values, but they allow objects to be modified by changing the stored value: the value itself cannot be modified. C++ also has references which are roughly pointers, variables also are really locations, not values, and the normal lvalue/rvalue crud in C and C++ is just a stupid confusion: in an l-context a variable name is associated with the location (address) of some store containing a value whereas in an r-context, the same variable refers to the stored value instead. The point is mutation cannot be defined for values, it is intrinsically a property of objects, whether the object is associated with a variable or with a pointer.

adamsol commented 3 years ago

How do you modify a tuple that is not stored in a variable?

There is always some "variable". If you have an array of tuples, you can modify some tuple in the array like this: array[0].a += 1. And the effect is exactly like if you created a new tuple and assigned it to the array element, just with much less boilerplate, and internally there is no copying or creating a new tuple object here.

As for value semantics, I mean that tuples are copied whenever they are assigned anywhere, just like in C++: https://isocpp.org/wiki/faq/value-vs-ref-semantics#val-vs-ref-semantics. Maybe it's not the best term, but from what I've learned there is always some ambiguity when referring to variable semantics. I can also see there is some problem with calling tuples "mutable", since they're not exactly like mutable objects or containers... but they really are mutable on the implementation level, they are just passed by value, not by pointer.

skaller commented 3 years ago

Ah, ok. Might be best to clarify the docs. Its also not a good idea to cite C++, since it is very confused and quite broken. Pass by value and pass by reference (meaning to a function parameter) is a horrible distinction that plagues most languages, if not in the core, then by virtue of library semantics. Perhaps this problem is absent in purely functional languages like Haskell.

Just FYI, in Felix I have finally solved this problem in the language core, but it took several decades to figure out how to do it. C basically got this right: if you want to pass by reference please use a pointer.

adamsol commented 3 years ago

Tuples are quite similar to basic types. Just like you can increment an integer variable, you can increment an integer inside a tuple. In both cases you have modified a variable, but haven't changed the values where that variable was previously used. So this is the value semantics, as opposed to reference semantics of containers or class objects.

I think the documentation describes the current behaviour well enough, and if it's not clear, there is still an example showing how it works.