If I give you a jar (or equivalent) of my compiled app, can you still apply cross cutting concerns (over compiled code you may not own)? Because that's what AOP and bytecode transformation can do.
You can't insert them at completely arbitrary points in third-party code. But libraries can be written generically (in terms of e.g. a general monad) so as to propagate cross cutting concerns from your callbacks to the right place, even when those concerns are implemented later on and the library doesn't know about them. Look at e.g. fs2 and http4s: everything's written generically in terms of some F[_] type which the user supplies when they use the library, and it will all be plumbed through correctly. So if you want logging, you use something like treelog as your F[_] type; if you want transactions, you include a transaction type in there. And so on.
In my experience that covers almost all the use cases, while it means that library code can still behave predictably and be understood and tested (e.g. you don't have to worry that a library upgrade is going to break your application, which you do if you've used AOP to define pointcuts deep in its internals).
I'm not familiar with scala but will take a look. Completely agree over your last point. I very much prefer libraries over frameworks, and as you said pointcuts are a bit fragile, but my main point is that they behave differently because of bytecode transforms.
You don't need macros to achieve what grandparent was talking about - handling cross-cutting concerns like logging or transaction boundaries without being too intrusive. In a language that lets you do a half-decent monad implementation (which is pretty much any language that has higher-kinded types and first-class functions, although do notation can be a useful enhancement), you can comfortably write this kind of thing in plain old code. (I do those very things - transaction boundaries and logging - in Scala all the time).
> More importantly, nothing about standard Java development requires The use of AOP.
And yet, in real-world Java development, people feel the need to use AOP. In my 10+ years of JVM work in companies large and small, I don't think I saw a single one that didn't use some form of AOP. That should tell you something.