[LRUG] The advantage of immutability?

jc at panagile.com jc at panagile.com
Thu Sep 10 02:34:02 PDT 2015


| I find jc's comment odd:


|

| So now that the problem is phrased in terms of wanting to manipulate shared global state then mutability seems like the correct option.




I’ll try to explain it better.




A lot of stuff has happened in the rails stack before you use an ActiveRecord model to update the database. Immutability is more obviously an advantage in other parts of the stack. For example, I think we typically expect Controllers to be immutable. If we call ‘request’ or ‘render’ multiple times we expect them to always behave in the same way and return the same result. I think you could make the same argument for params - it doesn’t feel right to me that you can change the params inside your controller.




In your question you only asked about ActiveRecord, or some ActiveRecord like ORM. You don’t have to write code like this. You could, for example, have created a ChangeSet object that stores a list of modifications that need to be made to the database. For me, it would make sense that this kind of object would be immutable. And this kind of object isn’t providing an abstraction for your database - for your global mutable state. Instead it’s designing your software from the outset to work with data that you don’t have a need to modify.




But you’re using Rails, and presumably using ActiveRecord, so you are using a framework that expects mutable objects. The API for ActiveRecord presupposes you will create and then mutate the objects. So if you ask ‘what are the advantages of immutable objects here?’ the answer is ’stop’. (The answer is gramatically incorrect but it’s still the right answer.)




This leaves you with a mutable Request object that you’ll use for other things, and I think that this permits a whole class of bugs that shouldn’t even be possible. I gave the example of passing it to a logging function and you rightly said that this shouldn’t change anything. So imagine part of your controller looks something like this:




def create

  @request = Request.find(params[:id])

  @request.update_attributes(:state => :denied)

  log(@request)

  # default render

end




And then your log method says something like:




def log(request)

  if request.state = :allowed # oops - typo

    # log something

  end

end




By the time you get to the view your request object will have changed to show the incorrect answer.




In simple code this isn’t much of a problem because simple code is, by definition, simple. But as code gets more complex then the number of potential errors like this increases, and they become harder to track down.




One of the big benefits of immutability is something called referential transparency. It’s a bit of a fancy sounding term, but it just means ‘when I call the same function with the same arguments it should always do the same thing’. This only really works if your code doesn’t have side effects - such as updating some kind of shared state. Once you allow mutable objects then you lose referential transparency and your code is harder to reason about. To understand what it does you not only need to understand that function, but also understand any other function that might have accidentally caused some side effect that affects the outcome.




Immutability isn’t always the correct answer. Sometimes you want state or other side effects and this introduces complexity in languages that only allow immutable data. You typically work around this by always passing ‘state’ into every function, and always returning a modified state. (In strongly typed languages this leads to monads, which are yet more complexity.) This is a different way of thinking about how you solve problems and, like any programming technique, it isn’t perfect for all situations.




Immutability is, in general, a good idea. It makes code easier to reason about. But like a lot of good ideas it doesn’t really work in Rails, especially in relation to ActiveRecord-like ORMs. I don’t think you are really asking ‘what are the advantages for immutable objects in web apps?’. You seem to be asking ‘what are the advantages for immutable objects when thinking about persistence in a rails-like framework?’ and the answer is ‘that’s not really appropriate’. This isn’t a bad answer - if your application is simple and you’re not having problems with accidental side-effects, or having problems reasoning about the code, then Rails with it’s mutable objects is probably a good solution. But IMO it becomes less good as your code becomes more complex, and making immutability the default can help keep your code manageable as it grows.




I hope that makes it a bit clearer.

On Thu, Sep 10, 2015 at 8:49 AM, Jonathon Horsman <jonathon at arctickiwi.com>
wrote:

> A lot of these solutions to updating an object seem to suggest "don't
> change the existing object but create a new object such as a 'response' or
> 'consent'".
> To me that is adding more complexity. If I want to display a list of these
> request objects and their current state I have to also query for their
> response object(s) and find the latest one for each.
> And it doesn't address the situation when a user updates their email
> address, presumably that will require an update to an existing user object?
> I find jc's comment odd:
> *So now that the problem is phrased in terms of wanting to manipulate
> shared global state then mutability seems like the correct option.*
> given that having a data store as a shared global state the situation in
> the vast majority of apps? Surely shoe-horning immutable objects onto a
> mutable data store would always be the wrong solution?
> Clearly I'm missing something - I appreciate your input.
> Jonno
> On 9 September 2015 at 19:21, Gerhard Lazu <gerhard at lazu.co.uk> wrote:
>> Should a Request know or even care about its denial? Which parts of your
>> app need to know about request denials?
>>
>> These questions are meant to open up the path to your answer. Simple
>> Design by Corey Haines will help.
>>
>> I can only reinforce Roland's point: keep it simple.
>>
>> Gerhard
>>
>>
>> On Wednesday, 9 September 2015, Jonathon Horsman <jonathon at arctickiwi.com>
>> wrote:
>>
>>> Hey smart people
>>>
>>> Can you shed some light on why an immutable object is an advantage for a
>>> web-based Ruby app?
>>>
>>> For example this app I'm working on has these Request things and a user
>>> has the ability to deny a Request.
>>>
>>> So the user would click a button which performs a post to the Request
>>> controller's deny action.
>>> If I were using Rails or some non-immutable based system I would fetch
>>> the object from the database, set it's status to denied, and save it.
>>>
>>> However since this Request is an immutable object, I have to either:
>>>
>>> 1. Write an update_status method which sets that value in the database,
>>> which becomes tedious when there's lots of possible attribute update
>>> combinations
>>> or
>>> 2. Read the object from the database, copy all the values to a new object
>>> with the denied status, and stick that back into the database. Seems like
>>> pointless overhead and could be dangerous if later that object gets another
>>> attribute which I forget to copy.
>>>
>>> My knowledge of immutable objects originates with Java Strings which I
>>> understand makes sense for performance and memory management reasons.
>>> I don't think this applies here though?
>>>
>>> Thanks!
>>>
>>> Jonno
>>>
>>
>>
>> --
>>
>> ------------------------------
>> Twitter <http://twitter.com/#!/gerhardlazu> Github
>> <https://github.com/gerhard> Blog <http://gerhard.lazu.co.uk/>
>>
>>
>> _______________________________________________
>> Chat mailing list
>> Chat at lists.lrug.org
>> Archives: http://lists.lrug.org/pipermail/chat-lrug.org
>> Manage your subscription: http://lists.lrug.org/options.cgi/chat-lrug.org
>> List info: http://lists.lrug.org/listinfo.cgi/chat-lrug.org
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lrug.org/pipermail/chat-lrug.org/attachments/20150910/f076c916/attachment.html>


More information about the Chat mailing list