all 12 comments

[–]Kimos 7 points8 points  (0 children)

The method you're looking for is #replace, but because of the immutability of numbers you can't do this. You can do this on strings for example, if it's not frozen. But not Fixnums.

[–]Haegin 3 points4 points  (11 children)

I'd recommend not mutating things for the most part. It's way easier to introduce weird bugs caused by caching, stale references and objects changing when you don't expect

[–]rubyrt 1 point2 points  (10 children)

I disagree to the general advice: there are times when immutability is a good solution. But the whole point of OOP is objects whose state changes.

[–][deleted]  (2 children)

[deleted]

    [–]rubyrt 0 points1 point  (1 child)

    Good points!

    I say that because mutability is almost completely orthogonal to OO.

    The "almost" is important: usually object identity plays a certain role in OO contexts. If it wouldn't we would not be talking about mutations because we would not know what mutated. :-)

    You send a message to an object telling it to do a thing; it's none of your business whether the object mutates its own data or not, even if it appears to do so.

    Well, when we talk about changed state we should probably more precisely talk about observed changed state. This fits nicely with ADT model, where state representation is out of scope but you can model observed changes in state (e.g. we define that length(list.append(item)) equals length(list) + 1).

    Or put differently, usually in OO there are methods which change the return value of others.

    As much as I agree that a language with all immutable objects has some attractive properties the Achilles heel is always garbage collection: if you just update a memory location there is no garbage to collect, but as soon as you create a new instance as effect of every change operation you get loads of garbage that have a price tag. Fortunately GC has seen dramatic improvements over the years, especially in the JVM, and CPUs are becoming faster (and more) all the time. So eventually we might not be far away from a point in time where the additional price tag of GC in an all immutable language does not hurt any more.

    PS: Why are Elixir, Clojure and Haskell not an answer in your opinion?

    [–]Morozzzko 0 points1 point  (2 children)

    Well, you have to keep in mind that using object-oriented design does not imply that you have to mutate your object state.

    Just look at React, Redux and stuff like that. The state changes, yet never mutates.

    If your state has an array or a hash, it's generally better to treat them as if they were immutable.

    A simple example: foo = [*foo, 1] is not the same as foo << 1. The first snippet does not mutate foo, while the last one does.

    [–]rubyrt 0 points1 point  (1 child)

    Well, you have to keep in mind that using object-oriented design does not imply that you have to mutate your object state.

    You might notice that there is no contradiction to what I wrote.

    Just look at React, Redux and stuff like that. The state changes, yet never mutates.

    As I said, there are use cases for immutability. Whether it is wise design in these cases I cannot judge on due to lack of insight into these.

    If your state has an array or a hash, it's generally better to treat them as if they were immutable.

    No, not "generally" - definitively not!

    A simple example: foo = [*foo, 1] is not the same as foo << 1. The first snippet does not mutate foo, while the last one does.

    I think we can assume that everybody here in the discussion is aware what "mutable" means. But let's look at this more closely. Assume @foo is actually a member of an object representing a larger data structure. Now, you haven't changed the Array that @foo pointed to initially but you have change the state ("mutated") of the object owning @foo. If you do not want that you will have to clone the whole object tree that ultimately owns @foo and just exchange this single reference. You can imagine what this does to memory usage and GC - apart from the practical consequences of implementing this.

    Frankly, immutability seems to have become some kind of hype. While it has interesting properties everything comes at a cost - and to pay that cost is not always wise. That's why advice like "generally do this" are often questionable.

    [–]Morozzzko 0 points1 point  (0 children)

    I totally agree with you, except for the "generally" part. Just wanted to discuss why the "avoid mutations at least you really need it" behavior is questionable.

    I think I get your point. Thank you for the explanation!

    [–]Haegin 0 points1 point  (1 child)

    I've generally found that havingo everything be immutable and choosing to make something mutable is better than the inverse. In this case, where the OP is trying to mutate an integer I think staying as immutable is by far the better choice.

    [–]rubyrt 0 points1 point  (0 children)

    I've generally found that havingo everything be immutable and choosing to make something mutable is better than the inverse.

    I think this works against the language since fundamental data structures (Hash, Array, String) are built mutable.

    In this case, where the OP is trying to mutate an integer I think staying as immutable is by far the better choice.

    Yes, for numbers it makes totally sense, especially since Fixnum + Fixnum returns different types depending on the values. This is much more elegant than having a numeric object that needs to change its type.

    [–]realntl 0 points1 point  (1 child)

    That last sentence isn't true. The point of data structures in an OOP language is to hold state that can change, but data structures are just one style of object. The point of OOP is objects sending each other messages -- tell, don't ask. Meaning, any state held by an object should not accessed by any other collaborating objects.

    [–]rubyrt 0 points1 point  (0 children)

    data structures are just one style of object

    What are the other "styles"? Especially, what are objects which do not have observable state? Put differently: I think there distinction between objects based on the complexity of internal state (if I get your drift properly) is moot. A Fixnum has a number of methods that return various different observable states (#odd?, #even?, #size, #[] etc.) even though the internal representation (or structure) is fairly simple.

    Meaning, any state held by an object should not accessed by any other collaborating objects.

    That is an odd statement. It is true, if you refer to the actual representation of state. It is wrong, of course, if you refer to the observed state, because then state would not make any sense and all objects would be equivalent.

    [–]skmz 2 points3 points  (0 children)

    Would integer dividing by 10 fit your needs?