It makes the code icky and hard to debug, and you can simply return new immutable objects for every state change.

EDIT: why not just create a new object and reassign variable to point to the new object

  • Dunstabzugshaubitze@feddit.org
    link
    fedilink
    arrow-up
    0
    ·
    11 days ago

    multiple other objects might be holding a reference to the object you want to change, so you’d either have to recreate those too or mutate them to let them point to the new object.

    however if you can do what you want to do in a side effect free way i suggest doing that, as it is indeed easier to reason about what happens this way.

  • FourPacketsOfPeanuts@lemmy.world
    link
    fedilink
    arrow-up
    0
    ·
    11 days ago

    Simply put, because you often want to change the state of something without breaking all the references to it.

    Wild off the top of my head example: you’re simulating a football game. Everything is represented by objects which hold references to other objects that are relevant. The ball object is held by player object W, player object X is in collision with and holds a reference to player object Y, player Z is forming a plan to pass to player object X (and that plan object holds a reference to player object X) and so on.

    You want to be able to change the state of the ball object (its position say) without creating a new object, because that would invalidate how every other existing object relates to the ball.

    • Giooschi@lemmy.world
      link
      fedilink
      English
      arrow-up
      0
      ·
      11 days ago

      What you need here is not the stability in memory (i.e. of pointers, which you lose when you recreate an object) but instead just the stability of an identifier (e.g. the index into a list).

      • tunetardis@lemmy.ca
        link
        fedilink
        English
        arrow-up
        0
        ·
        11 days ago

        Well, but then you’re basically just pushing the mutability onto the container, since you need to be able to replace elements within it.

        It’s a good strategy at times though. Like say you’re working in a language where strings are immutable and you want a string you can change. You can wrap it in a list along the lines s=['foo'] and pass references to the list around instead. Then if you go s[0]='bar' at some point, all the references will now see ['bar'] instead.

        • Giooschi@lemmy.world
          link
          fedilink
          English
          arrow-up
          0
          ·
          10 days ago

          Well, but then you’re basically just pushing the mutability onto the container

          That’s the point, when programming with immutable structures you always pass the mutability onto the enclosing structure.

          It’s a good strategy at times though. Like say you’re working in a language where strings are immutable and you want a string you can change. You can wrap it in a list along the lines s=['foo'] and pass references to the list around instead. Then if you go s[0]='bar' at some point, all the references will now see ['bar'] instead.

          A list is an antipattern here IMO. Just wrap it in some dedicated object (see e.g. Java’s StringBuilder).

          • tunetardis@lemmy.ca
            link
            fedilink
            English
            arrow-up
            0
            ·
            10 days ago

            That’s the point, when programming with immutable structures you always pass the mutability onto the enclosing structure.

            I guess the point I was trying to make here was if the data type is already mutable, there is no point in sticking it in a list just so you can replace a reference with an identifier. You’re just adding an extra level of indirection. But sure yeah, if the type is inherently immutable, you have to do something.

            A list is an antipattern here IMO. Just wrap it in some dedicated object (see e.g. Java’s StringBuilder).

            Interesting. I’m not aware of anything like StringBuilder in the standard library for either Python or JavaScript. Looks like it wraps a list of characters and tries to behave as string-like as possible? You could presumably write your own class like that or download an implementation from someplace.

            I guess in most cases in my own code, where I need a mutable string is usually as part of a larger data structure which is the thing that gets passed around by reference, so it’s easy enough to replace a field within that.

            For building up a string, I would tend to use an io.StringIO in Python with file-writing calls, but those aren’t meant for sharing. What you don’t want to do is use the += operator a lot on strings. That gets expensive unless strings are mutable (like they are in say C++'s std::string).

      • Kacarott@aussie.zone
        link
        fedilink
        arrow-up
        0
        ·
        10 days ago

        This is close, but as someone already said, an index into a list just means you are mutating the list.

        Your stable “identifier” needs to be a function, ie. a reused design pattern. Compared to the list, this would be an index function which gets an element from an arbitrary list, meaning you don’t have to mutate your list anymore, you just build a new one which still works with your function.

        This is why languages which avoid mutation and side effects are always (to my knowledge) functional languages.

  • tunetardis@lemmy.ca
    link
    fedilink
    English
    arrow-up
    0
    ·
    11 days ago

    As others have pointed out, there is the issue of breaking references to objects.

    There can also be a lot of memory thrashing if you have to keep reallocating and copying objects all the time. To some extent, that may be mitigated using an internment scheme for common values. In Python, for example, integers are immutable but they intern something like the first 100 or so iirc? But that doesn’t work well for everything.

    Any container you want to populate dynamically should probably be mutable to avoid O(N²) nastiness.

  • thenextguy@lemmy.world
    link
    fedilink
    arrow-up
    0
    ·
    11 days ago

    Faster. Less memory. Maps to physical things well (e.g. a device with memory mapped registers). No garbage collection / object destruction needed. No need to initialize new objects all the time.

  • FizzyOrange@programming.dev
    link
    fedilink
    arrow-up
    0
    ·
    11 days ago

    Yeah the main reason is performance. In some languages if you use a value “linearly” (i.e. there’s only ever one copy) then functional style updates can get transformed to mutable in-place updates under the hood, but usually it’s seen as a performance optimisation, whereas you often want a performance guarantee.

    Koka is kind of an exception, but even there they say:

    Note. FBIP is still active research. In particular we’d like to add ways to add annotations to ensure reuse is taking place.

    From that point of view it’s quite similar to tail recursion. It’s often viewed as an optional optimisation but often you want it to be guaranteed so some languages have a keyword like become to do that.

    Also it’s sometimes easier to write code that uses mutation. It doesn’t always make code icky and hard to debug. I’d say it’s more of a very mild code smell. A code musk, if you like.

  • 4am@lemm.ee
    link
    fedilink
    arrow-up
    0
    ·
    10 days ago

    There are times when immutable objects are absolutely the way to go from a data safety perspective. And there are other times when speed or practicality prevail.

    Never become an extremist about any particular pattern. They’re all useful - to become a master you must learn when that is.

  • Shanmugha@lemmy.world
    link
    fedilink
    arrow-up
    0
    ·
    edit-2
    10 days ago

    Logical and human friendly answer: mutable objects are not a problem, poorly designed code is

    Personal rant: why even bother with objects, just use strings, ints, floats, arrays and hashmaps (sarcascm. I have spent hours uncovering logic of large chunks of code with no declaration of what function expects and produces what)

    And also, seeing endless create-object-from-data-of-other-object several times has made me want to punch the author of that code in the face. Even bare arrays and hashmaps were less insane than that clusterfuck

  • Michal@programming.dev
    link
    fedilink
    arrow-up
    0
    ·
    edit-2
    10 days ago

    Because recreating entire object just to make a single change is dumb.

    God help you if you’ve already passed the object by reference and have to chase up all the references to point at the new version!

    • sudo@programming.dev
      link
      fedilink
      arrow-up
      0
      ·
      10 days ago

      You can safely do a shallow copy and re-use references to the unchanged members if you have some guarantee that those members are also immutable. Its called Persistent Data Structures. But that’s a feature of the language and usually necessitates a GC.

  • BehindTheBarrier@programming.dev
    link
    fedilink
    arrow-up
    0
    ·
    10 days ago

    Try making a list without copying every time you add something. Mutability matters then. Imagine copying 10000 elements, or copying 10000 references to items every time something were to be added or changed.

  • wewbull@feddit.uk
    link
    fedilink
    English
    arrow-up
    0
    ·
    10 days ago

    You can do exactly as you say, and you’re right - it makes code easier to reason about. However it all come down to efficiency. Copying a large data structure to modify one element in it is slow. So we deal with the ick of mutable data to preserve performance.

  • Traister101@lemmy.today
    link
    fedilink
    arrow-up
    0
    ·
    9 days ago

    So your writing a game. This game has what I’m going to call “entities” which are the dynamic NPCs and such objects. So these objects are most easily conceptualized as mutable things. Why mutable? Well they move around, change states depending on game events ect. If this object is immutable you’d have to tie the in world representation to a new object, constantly just because it moved slightly or something else. This object is mutable not just because it’s easier to understand but there are even efficiency gains due to not needing to constantly create a new version just because it moved a little bit.

    In contrast the object which holds the position data (in this case we’ll have 3 doubles x, y, z) makes a lot of sense as an immutable object. This kind object is small making it cheap to replace (it’s just 3 doubles, so 3*64 bits or a total of 24 bytes) and it’s representing something that naturally makes sense as being immutable, it’s a set of 3 numbers.

    Now another comparison your typical dynamic array type container (this is your std::vector std::vec ArrayList and friends). These are mutable objects mainly due to efficiency (it’s expensive to copy the contents when adding new values) yet they also are easier to conceptualize when mutable. It’s an object containing a collection of stuff like a box, you can put things in, take stuff out but it’s still the same box, just it’s contents have changed. If these objects are immutable to put something into the box you must first create a brand new box, and create a copy of the old boxes contents, and then put your new item into the box. Every time. Sometimes this kind of thing makes sense but it’s certainly not a common situation.

    Some functional languages do have immutable data structures however in reality the compiler usually does some magic and ends up using a mutable type as it’s simply so much more efficient.

  • DerArzt@lemmy.world
    link
    fedilink
    arrow-up
    0
    ·
    9 days ago

    For all the people in this thread talking about the inefficiencies of immutability, they may find this talk by Rich Hickey (the creator of clojure) interesting. Not so much as it shows that they’re wrong, but more so that it’s a good lecture explaining how we can build immutable data structures that address the limitations immutability in a way that reduces the overhead.