4 ms·
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
by endgame 4y ago
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"), you can use a library of transformations like Haskell's Control.Lens.Plated module from package "lens" and write queries and transformations over arbitrary structures in a very compact way.
I have used this a few times: some examples include walking complex documents to extract particular information from tables, or rewriting every "import" node in a syntax tree, but leaving the rest of the program untouched.
To support Traversals in Java under that get/set form, you would probably need get/set members that were functions to/from T and Array<T>, and then you'd have to write separate compose operators for each composition:
- lens + lens = lens
- lens + traversal = traversal
- traversal + lens = traversal
- traversal + traversal = traversal
This is one reason that Haskell's "lens" package has that funky type alias for Lens instead of a record-of-functions, and a mature lens library is one of the main reasons that Haskell is my favourite general-purpose programming language.
You _can_ force such a take on optics into Java. Someone found an implementation of profunctor optics in Minecraft's DataFixerUpper: https://www.reddit.com/r/programming/comments/9lyplq/microsoft_open_sources_parts_of_minecraft_java/e7aids2/ https://www.reddit.com/r/programming/comments/9lyplq/microso... That subthread has some really good meaty comments in it, if you're interested in this sort of thing.
- pron 4y ago> (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 effort-saving worthwhile?
- chowells 4y agoMaybe 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.
- ivan_gammel 4y agoI think there’s some misunderstanding between the two of you. Reflection is indeed the sane and convenient solution for working with structures when type is not known at compile time (e.g. general purpose library for DI, ORM, security or serialization). It’s definitely not the way to manipulate object trees when type is known in compile type. However the point about complexity is valid: is it really worth patching strongly coupled solution violating SRP and breaking encapsulation with some design pattern for an anti-pattern (setters)?
- pron 4y agoRecords were specifically designed for safe reflection (and safe serialization). What causes security vulnerabilities is so-called deep reflection, i.e. the use of setAccessible, and can then violate various invariants. That's not what I'm referring to here. Reflection ≠ deep reflection.
- kqr 4y agoPart 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.