3 ms·
I don't think I understand. We're talking about how to implement this. That method will still have to use deep copying + mutation, lenses or something else inte
by ImprobableTruth 4y ago
I don't think I understand. We're talking about how to implement this. That method will still have to use deep copying + mutation, lenses or something else internally.
- ivan_gammel 4y agoYou do not need lenses for implementation of a method working with internal state, that will be a gross over-engineering. record Order(List<Approval> approvals, int version, OrderStatus status) { Order confirmed(Instant timestamp, UUID approver) { var approvals = this.approvals.stream().map(a-> a.user().uuid().equals(approver) ? a.confirmed(timestamp) : a).toList(); return new Order(approvals, version++, OrderStatus.APPROVED); }
- ImprobableTruth 4y agoLenses are an abstraction of what you mentioned. For a single example it's of course overengineering. The benefit is that e.g. when you have a bunch of methods like that, you can avoid duplicating the code that is responsible for copying the inner layers. Otherwise if you e.g. add a layer, you have to touch all those methods.
- ivan_gammel 4y agoIf you look at my example you will notice that it has zero lines that would be duplicated in real life scenarios, because it does not perform a deep copy (a benefit of using immutable objects).
- ImprobableTruth 4y agoImagine you changed it from an array of approvals to a single one like in the original example - you'd need to make a change in every method of that type (replacing the map). That's code duplication, it's just not really obvious yet because there's only two layers to pass through. Per layer you need a constructor call (or map to copy & modify the array). As a more obvious example, if you want to modify a.b.c.d.e (which isn't unrealistic), you'll need to call the constructors of A, B, C and D. If you don't use lenses, this is the code that will be duplicated. You can spread it between the classes or do all that in a method of A, but if you want to also modify a.b.c.d.f, you'll need to duplicate all that code (add another method to A, B, C, D that each calls the constructor). With lenses, you define once how to access d from a and then any modification of d can happen through that. If the structure changes, you only need to do the changes once by modifying the lens.
- ivan_gammel 4y agoThose things are easy to imagine, however: 1) change in cardinality is such a change in domain that lenses won’t solve it. There will be much bigger changes in business logic probably making original code obsolete regardless of used pattern. 2) deep tree modifications of the kind that you mentioned indicate problems with architecture. Why would you need the whole typed tree (not DOM or something, but object tree) to modify a tiny leaf of it? If you touch multiple leafs with root as closest integration point, why your data model is designed like that, pointing to strong coupling of different contexts? 3) most importantly the code that can be reused, can be extracted to a private method of an entity where the change occurs. Calling nested constructors on the root object breaks encapsulation — it should pass the message to nested objects instead.
- ImprobableTruth 4y agoIf you have an immutable structure, you can't just modify the leaf of it. Unless you're arguing that deep, immutable structures are an issue in themselves?
- ivan_gammel 4y agoOh, we cannot modify it, of course, but we are talking about the ways of producing a modified copy. The part of the argument about deep immutable structures is just a side remark: building them for modification is likely pointing to a problem with design, but that’s not the most important part. The important part is that it is better to call business methods than externalizing the whole thing with deconstruction-modification-construction to a lens or some other code, when you have the full power of OOP to do it right.
- deleted 4y ago[deleted]