Live data from Hacker News

Making Lenses Practical in Java

chriskiehl.com

91–100 of 108 posts

Re: Making Lenses Practical in Java

#91

Earlier quoted context omitted.

I don’t think properties add anything that getters/setters don’t already give you (other than slightly different syntax). I think the “right” way to replace the need for builders is to add support for named arguments.

> I don’t think properties add anything that getters/setters don’t already give you Except that you have to write them out by hand for every property you want in your code. Or generate them. > I think the “right” way to replace the need for builders is to add support for named arguments. Often you want to add validators to your setters. Just named arguments won't cut it.

> Except that you have to write them out by hand for every property you want in your code. Or generate them.

Our analysis has determined that the vast majority of getters and setters are used in classes that are better replaced by records anyway (with all the benefits records bring that go far beyond conciseness, such as safe serialization and correct interaction with collections). The remaining cases are not numerous enough to pose a large enough problem that justifies an ad-hoc language construct, but could be further helped by a more general mechanism, such as concise method bodies (https://openjdk.org/jeps/8209434). The result is a smaller number of much more powerful features.

In other words, rather than making getters and setters easier to write, we simply get rid of the need for most of them altogether, which not only saves us a language feature, but also the downsides that setters and getters (or properties) have. We preferred tackling the problem at its source by asking what causes the need to write so many getters and setters in the first place (Java's lack of good data manipulation constructs) rather than treating the symptom (writing getters and setters is tedious).

Re: Making Lenses Practical in Java

#92

Earlier quoted context omitted.

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 pr…

For the example provided I would encapsulate that logic in the object itself returning a confirmed copy as a return. And as someone else pointed out I also much prefer explicit code where you easily can understand what is going on…

I agree that I'd approach this in the form of (`val completed = order.approveAs(currentUser, Instant.now())` or maybe `val completed = orderService.approve(currentUser, Instant.now())`) myself. That doesn't require any specific programming language either. I was just using the example in the blog.

However, I don't see Kotlin as a language that sticks to explicit code where you can understand what is going on. Between extensions, companion objects, infix methods and other syntactic sugar, I would say Java is much better in terms of code explicitness.

Re: Making Lenses Practical in Java

#93
post #60

Earlier quoted context omitted.

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 pr…

I like to call this "direct code". It does exactly what it looks like it does. Lenses do not, they introduce a whole abstraction around something as simple as get/set. For this reason, the Kotlin code above would be much preferred IMO.

I agree, but the Kotlin code isn't that different from the Java code in the blog. The biggest difference is that Java lacks the necessary built-in support and relies on Lombok to provide the necessary copy helpers, but I can't think of a reason for a Java project not to use Lombok.

I see Kotlin as better Java, but with the right compiler extensions (Lombok, Manifold) you can turn Java into a pretty modern language as well, if you're not writing legacy code in Java 8/9/11. The specific approach this article is about doesn't really put Kotlin at an advantage, nor does it place it at an advantage. You might as well write the Lens pattern in Python or Rust, the idea is still the same.

Re: Making Lenses Practical in Java

#94

Earlier quoted context omitted.

I don’t believe this is true. From my reading of the document, all with expressions go through the primary constructor.

Well, that is exactly the problem, as I already explained a few times in this thread. Constructor cannot validate state transitions, so the “with” statement or the block surrounding it will have to do it, breaking encapsulation and likely requiring multiple copies of this code.

But if that’s the case, then a record is the wrong data type for what you’re doing anyway, since you can call the primary constructor at any point with those same un-validatable values.

It’s not “with” that’s problematic. If a record doesn’t work for what you’re trying to do, just use a class.

Though I will say that this is why I generally think ML’s (i.e. OCaml, Standard ML) approach to encapsulation with modules is generally superior to using classes for encapsulation.

Re: Making Lenses Practical in Java

#95

Earlier quoted context omitted.

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…

If 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?

Re: Making Lenses Practical in Java

#96
post #9

I don't agree on the fact that lombok has brought us out of the dark ages. We used to use it, but it has some drawbacks. One of them, it's an additional dependency. This, for a simple thing such as pojo, seems a bit overkill to me. The additional amount of time used to write some cose isn't worth the risk of additional bugs hidden in having one more dependency.

Lombok is a compiler extension, not a dependency. It does actually need to be present at runtime!

If it's not included in the base java compiler it's a dependency.

Re: Making Lenses Practical in Java

#97
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.

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 d…

> Maybe in the open source world?

Here's a report from New Relic about Java use among their customers (who write closed source) from a year ago, and the Java 8 exodus is continuing: https://newrelic.com/resources/report/2022-state-of-java-eco....

Java 8 is still definitely used in applications that no longer see much development, and it will be some time for before it mostly disappears, but those applications don't benefit from new libraries either.

Re: Making Lenses Practical in Java

#98
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…

Withers invoke the constructor. E.g.

    r = r with { state = state + C; };
compiles to:

    r = new R(r.state() + C);
All state transitions of ADTs can be checked in constructors.

Re: Making Lenses Practical in Java

#99

I just use Kotlin. Never looked back.

How do you update a deeply nested immutable type in kotlin?

Lenses work today. And I think the value class feature will be awesome.

https://github.com/Kotlin/KEEP/blob/master/notes/value-class...

Re: Making Lenses Practical in Java

#100
post #9

I don't agree on the fact that lombok has brought us out of the dark ages. We used to use it, but it has some drawbacks. One of them, it's an additional dependency. This, for a simple thing such as pojo, seems a bit overkill to me. The additional amount of time used to write some cose isn't worth the risk of additional bugs hidden in having one more dependency.

The risk of bugs because you forgot to update a setter after changing a field or something like this is way higher than the risk of bugs in Lombok.

Risk is measured by multiplying likelihood with impact. A single setter, low impact, a single bug in lombok, or misused feature, impacts a lot
Post reply on HN