> This is more readable than both
Except you sneak in alien symbols: +, - (used both as a unary and a binary operator!), *, ^, and /. See how readable they make things, though? :)
Your quadratic_roots is, indeed, nice in isolation. I'd even go as far to say that it's a good pedagogical piece. However, in production code pedagogy is not what I want to optimize for. Production code often repeats patterns with slight variations on a theme: What if you only want real roots? What about complex a and b? What about just grabbing the discriminant?
It's easy enough to modify quadratic_roots, but then you either get a combinatorial explosion of function definitions, or you end up with a function with extra parameters to select the different variations, and you often end up with a deeply nested function call graph, e.g. replacing sqrt(b^2 - 4*a*c) with discriminant(a, b, c), which makes quadratic_roots more annoying to read.
In practice, defining a function or some abstraction barrier is making an architectural decision. Ideally that decision would correspond perfectly to some fundamental feature of the problem you're trying to solve, but in practice we rarely are coding with perfect problem domain knowledge, right? In practice, our functions/classes/abstractions end up accumulating cruft, right? Why is that?
Where we traditionally handle complexity by setting up abstractions to let us hide parts of that complexity, APL is good at using a different tactic, called "subordination of detail." Done well, this looks like writing very simple, direct code that empowers your basic language tools to take on domain-specific meaning without introducing any abstractions.
Here's an example that I came across recently: t[p]=E. The primitive operations are simply equality comparison (x=y) and array indexing (x[y]). However, in the specific problem, t[p]=E effectively selects parts of an AST that correspond to expressions. It's just a friggin' index operation and equality comparison! Normally this kind of operation would hinge on a reasonably large hierarchy of datatype definitions, traversal patterns, and hard-to-read performance hacks.
Instead, the APL (t[p]=E) is crazy short, obvious in meaning, and you can literally just read the computational complexity right from the expression! What other language does that? Granted, you gotta learn a little APL first.
> The thing that array languages get wrong is that code is read more often than it is written
Personally, I find APL quite pleasant to read. You really should give it an honest try.