Live data from Hacker News

Making Lenses Practical in Java

chriskiehl.com

41–50 of 108 posts

Re: Making Lenses Practical in Java

#41

Earlier quoted context omitted.

Lenses 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.

If 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).

Imagine 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.

Re: Making Lenses Practical in Java

#43
post #32
post #26

Earlier quoted context omitted.

The real power IMHO comes once you start using traversals, which are like lenses but focus 0..n elements instead of exactly 1, and compose well with themselves and with lenses. (It gets better still after that, as you add in Folds, Isos, Prisms, etc., but we'll leave those for now) Once you have a traversal that can pull out immediate children of the same type (e.g., "given a DOM node, traverse all of its children"),…

> (e.g., "given a DOM node, traverse all of its children") you can use a library of transformations ... and write queries and transformations over arbitrary structures in a very compact way. But in Java you'd do that with reflection. So the question remains, how many such different queries/transformations you actually have (in the source code, where type checking is available, rather than user input) to make the effo…

Maybe you would rather do that with reflection, but I'd rather do something that isn't fundamentally the same tools that create security vulnerabilities. Every time you can not-use reflection is a win.

Re: Making Lenses Practical in Java

#44
post #34

Earlier quoted context omitted.

In the works: https://github.com/openjdk/amber-docs/blob/master/eg-drafts/...

I hope they will find a better solution, as this proposal will break encapsulation. Let’s say you model allowed transitions via business methods and thus restrict certain state changes. You can either serialize or deserialize state or perform an allowed transition. If wither becomes a language feature, it will allow forbidden state changes that cannot be caught by validation in constructor, which will allow deseriali…

[deleted]

Re: Making Lenses Practical in Java

#45
post #33

Earlier quoted context omitted.

But they require a new JDK version, and a surprisingly high amount of Java projects are still stuck on JDK8.

JDK 8 is now a minority, and if we count only projects that are still undergoing significant development it's a significantly smaller minority. The main reason some projects are still stuck on 8 is because they use old libraries that have made breaking changes, and don't have resources to adapt their use, let alone take on new ones.

Or they develop for Android. Not a minority.

Re: Making Lenses Practical in Java

#46
post #33

Earlier quoted context omitted.

But they require a new JDK version, and a surprisingly high amount of Java projects are still stuck on JDK8.

JDK 8 is now a minority, and if we count only projects that are still undergoing significant development it's a significantly smaller minority. The main reason some projects are still stuck on 8 is because they use old libraries that have made breaking changes, and don't have resources to adapt their use, let alone take on new ones.

Maybe in the open source world? In some closed source company codebases that are known to me, there were still million lines of code on JDK8, since efforts to port everything to a newer JDK don't necessarily enjoy a high priority.

As an application developer in such an environment one then might even not have the possibiltiy to pick the JDK version of their choice on their own, since as you mentioned there might be dependencies which haven't been updated. In such an environment solutions which don't require a JDK upgrade (Lombok, Kotlin, etc) can certainly help developers.

If the upgrade is feasible, I would definitely use the new builtin language features.

Re: Making Lenses Practical in Java

#47
post #45
post #33

Earlier quoted context omitted.

JDK 8 is now a minority, and if we count only projects that are still undergoing significant development it's a significantly smaller minority. The main reason some projects are still stuck on 8 is because they use old libraries that have made breaking changes, and don't have resources to adapt their use, let alone take on new ones.

Or they develop for Android. Not a minority.

Are most android devs still working with Java or Kotlin?

Re: Making Lenses Practical in Java

#48

I just use Kotlin. Never looked back.

Kotlin alone won't implement these features for you. You'd end up with something like this:

    val order = loadOrder()
    
    val approved =
        order.copy(
            approval = order.approval.copy(
                confirmation = order.approval.confirmation.copy(updatedOn = Instant.now()),
                status = ApprovalStatus.Approved
            )
    
I think I prefer this explicit deep cloning over the automagically generated lenses in standard code, but I can see the appeal. The problem is that mutated copies of immultable objects with deeply nested properties that need to be updated just requiresa lot of code.

On the other hand, if I were to design the application in Kotlin, I wouldn't use such a deeply nested object graph if I was going to constantly duplicate theses objects. I also think constantly creating and destroying immutable objects is a sign of immutability being used in the wrong place; I don't know why you wouldn't simply mutate state if the goal is to mutate the state. With Kotlin, but also Java, you could then very easily achieve these changes.

Lenses are available as a package for both Java and Kotlin so the concept is hardly language specific. People find a use for them in both languages, even if I don't see the point, and that means just switching to a better language isn't the solution here.

Re: Making Lenses Practical in Java

#49
post #32
post #26

Earlier quoted context omitted.

The real power IMHO comes once you start using traversals, which are like lenses but focus 0..n elements instead of exactly 1, and compose well with themselves and with lenses. (It gets better still after that, as you add in Folds, Isos, Prisms, etc., but we'll leave those for now) Once you have a traversal that can pull out immediate children of the same type (e.g., "given a DOM node, traverse all of its children"),…

> (e.g., "given a DOM node, traverse all of its children") you can use a library of transformations ... and write queries and transformations over arbitrary structures in a very compact way. But in Java you'd do that with reflection. So the question remains, how many such different queries/transformations you actually have (in the source code, where type checking is available, rather than user input) to make the effo…

Part of the convenience is in having uniform syntax for data access and modification, no matter what the target is.

Is it "worthwhile" to use this when you have it? No doubt in my mind.

Is it worthwhile to add a build time dependency for it? Maybe depends on application.

Re: Making Lenses Practical in Java

#50

Earlier quoted context omitted.

If 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).

Imagine 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 (whi…

Those 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.

Post reply on HN