Search code examples
javascriptfunctional-programminglodashimmutability

How do immutable methods work in lodash fp?


I'd like to understand how the immutable methods of lodash/fp work.

Do they deeply clone a collection before changing it or do they implement kind of a structural sharing between the two versions of the object?

I have tried to understand it "from the outside" and I couldn't come to a clear conclusion.

In the first case below, mutating an updated collection doesn't impact the original collection.

However, in the second case, mutating an updated collection does impact the original collection.

var fp = _.noConflict();

var data = { a: { c: {} } };
var updatedData = fp.set(["a", "c", "d"], 5, data);
updatedData.a.c.e = 9;
console.log(updatedData.a.c.e, data.a.c.e);

var data2 = { a: { c: [] } };
var updatedData2 = fp.set(["a", "d"], 5, data2);
updatedData2.a.c[0] = 9;
console.log(updatedData2.a.c[0], data2.a.c[0]);
<script src='https://cdn.jsdelivr.net/g/[email protected](lodash.min.js+lodash.fp.min.js)'></script>

I have also posted the question on Lodash github.


Solution

  • Do they deeply clone a collection before changing it or do they implement kind of a structural sharing between the two versions of the object?

    Yes, they use a structural sharing.

    We can see in this example that the c object is reused, not cloned. This is useful because it could be very large in size (in my case it contains the entire window object!).
    You can imagine attempting to deep-clone it would be a waste of resources.

    var fp = _.noConflict()
    var data, updatedData
    
    
    data = { a: { c: { window: window } } }
    updatedData = fp.set(["a", "d"], 5, data)
    console.log(updatedData.a.c === data.a.c)
    <script src='https://cdn.jsdelivr.net/g/[email protected](lodash.min.js+lodash.fp.min.js)'></script>

    Only the "parent" objects are being changed, so they must be cloned. The leaves which are not part of the mutation do not need to be changed.

    The reason it has gone wrong for you in your example is because you are combining immutable and mutable code, which is not a good idea. These cloning optimisations will obviously go wrong if you mutate code that was expected to be immutable.