Ya know the more I reflect on the languages I know the more I realize that outside of functional languages none of them really handle immutability well.

Consider that you want most of your objects to be immutable most of the time. Thats all well and good till you realize you want to be able to edit the objects in such a way that it creates duplicates that have some data changed but are likewise immutable.

This tends to stop working, almost entierly, once you get into subclassing. If you parent class has a method that returns a copy of itself with some data modified, this will break in children classes, since you want children classes to return instances of itself, not its parent.

Its not that you cant fix that, but the code gets very ugly very quickly. Generally you are forced to let the code handling the classes do the copying and editing itself, but that is pretty ugly too.

I have had this pattern problem in almost every OO language i messed with, Java, Ruby, Python, etc.

@freemo Off the top of my head, I think this would work pretty well if you use a frozen data class. Then in your manipulation methods you can use .replace (docs.python.org/3/library/data) to do the manipulations without modifying the current instance. I think it would work well with subclasses as well because it will always be calling the current class constructor.

@multimeric That is a bit limiting since you can only specify fields. In an ideal world youd have a frozen class with methods that can do complex manipulations returning copies rather than being limited to dealing with fields.

You could of course always clone, modify, and refreeze.

But yea there are solutions (this being one of them)... none of them feel very elegant to me.

@freemo I don't follow. Isn't your entire state made up of fields? What else needs to be changed? If you define all of your methods in terms of .replace instead of modifying self, it seems like it should work.

@multimeric The state is made of fields sure, but I usually dont want to manipulate the fields directly all in one place. Usually the fields would get manipulated through some methods attached to the object and usually in a way that will traverse many objects during the process. If I were dealing with a simple object with values then the problem wouldnt be much of a problem to begin with.

@freemo If you consistently use the pattern of returning .replace() for each of your manipulations, you can still use abstractions and don't have to modify everything in one place. You can also modify associated objects via this mechanism:

@dataclass(frozen=True)
class Car(Vehicle):
def service(self) -> Self:
return self.replace(
wheels = self.wheels.service(),
battery = self.battery.service()
)

Follow

@multimeric Yea thats one solution, but thats a lot of additional code you now need to do, and doesnt give you the option of switching between mutable and unmutable versions without more code.

Again there are solutions, but most of them arent very elegant.

In an ideal world an object would be mutable by default, but can be made to be immutable. You pick what makes sense for the scenario. Ideally without needing to write a lot of boilerplate code. For this solution you'd need different methods entierly for the mutable way of changing an object vs the immutable change-and-copy way.

Sign in to participate in the conversation
Qoto Mastodon

QOTO: Question Others to Teach Ourselves
An inclusive, Academic Freedom, instance
All cultures welcome.
Hate speech and harassment strictly forbidden.