> In 2002, the visitor approach used by ASM seemed clever
I couldn't agree more. The visitor pattern was very hard to explain/justify back then, and still difficult to explain to newbie programmers just entering the profession.
Looking at the examples, I think this is going to be an official replacement for ASM, meaning it's going to be pretty low level. The use of streams pretty straightforward.
If anyone from the JEP is reading this: I have two pieces of feedback!
First, take some inspiration from the way CDI Portable Extensions work. This is probably the most delightful extension API I've ever used. The @Observe callbacks are super simple to explain to people and it's really easy to write extensions for the framework.
Next, I wouldn't ignore the need for a higher-level API akin to ByteBuddy or Javassist. Sometimes I just want to write an interpreter or intercept a method call and thats it.
For example in my Junit/Mockito extension https://github.com/exabrial/mockito-object-injection I need to intercept a call to the class under test in order to lazily inject dependencies at the last possible moment. While I certainly could do this with ASM, Javassist makes this fairly simple with it's MethodHandler api.
Side note, it's a damn shame we don't have a mobile operating system that is JVM native :/ All this cool APIs simply never reach a huge number of devices.
It's way too late, the API is effectively locked in preview mode.
While I am not a fan of ASM's visitor pattern, explaining it to anyone would be the least of my concerns, ASM requires pretty extensive knowledge on class structure, method signatures, and most of the byte code instructions. Whoever ventures in byte code editing mode should be able to read the byte code directly.
When you write a CDI portable extension, you register a bunch of observation handlers. So as the CDI environment is discovering stuff, it calls your observers and you have the chance to make chances to the runtime. I was thinking something akin to that here... when reading a class, your observers get called and you have a chance to manipulate the class data.
I find this style intuitive.
Edit to add: slides here https://cr.openjdk.org/~psandoz/conferences/2023-JVMLS/Code-...
Well currently you can't get at the AST, only (with some hairy code) bytecode. (The use cases are post-compilation)
But suppose this work did enable that. What Paul is saying is that there are intermediate representations between bytecode and AST that are more helpful for these GPU / SQL / whatever runtime compilers. Representations that capture dataflows, for example. Chance are these compilers would transform an AST to something like this anyway.
See https://mlir.llvm.org/ which is referenced in that talk.
"We designed it as a functional library because functionally inspired libraries are successful at meeting these goals."
Ironic.
Scala monads are fantastic IMO; Either-based programming is simple and eliminates tons of boilerplate. So is Cats, although the learning curve is steep.
One of the big headaches we see with moving up JDK version is bytecode generation/manipulation libraries choking on newer versions of the JDK. It's generally a simple update, but sometimes it's not (for example, when someone is shading asm).
Having bytecode generation as part of the JDK will mean all libraries, including asm, can migrate to that and benefit from a perpetually supported API that updates as the JDK does.
This is probably 80% of the headaches I've experienced going from the likes of Java 11 to 17 and 17 to 21.
Lombok wouldn't be nearly as troubled if it was just doing simple bytecode manipulation.
If lombok wants to stop the pain, then they'll need to stop reaching into internal APIs. They'll possibly need to remove a few features (like some of the `private final` work they are doing).
In other words, you can expect lombok to be a headache for years to come. (Maybe consider not using it? That'd be swell. Speaking as someone that curses lombok everytime jdk updates roll around.)
Even before records, just use public final fields, and be done with the getter/setter nonsense. (IDEs do a good job of offering options for toString(), and c-tors)
Personally, I consider lombok one of the better anti-patterns widely used.
I personally use the @Builder annotation on records with more than 3-4 fields. I find it much more readable than a long list of arguments to the constructor.
It also makes it easy to return a copy of the record where only a few fields have changed:
var r = book.toBuilder()
.lastUpdated(now)
.title("...")
.build()
I also use other annotations, but I could work without them if a future version of Java provides a builder-like pattern (or named arguments)Unlike compiler extensions, Lombok compiles source files that do not conform to the Java language specification. Lombok is an alternative Java Platform language, like Clojure or Kotlin or Scala, except that it's a superset of the Java language. However, rather than forking `javac` source code and modifying it to compile Lombok source files, the Lombok compiler modifies `javac`'s operation by hacking into its internals and modifying them as it runs to compile Lombok sources rather than Java sources.
Having alternative Java Platform languages is perfectly fine. The problem with Lombok is that it doesn't present itself as such but as a library or a compiler extension even though it violates the Java language specification in ways that compiler extensions are forbidden from doing.