| One thing humans are good at is pattern recognition. Terse APL is - once you are used to it - very recognizable. Many constructs take less characters to algorithmically specify than to name: (Examples in K because that’s what I know best): (+/x)%#x computes the average of a vector x; or the average of each column in a 2D matrix x; or other averages for other constructs. It takes about as much characters to spell “average”, which is considers too short a name in modern C or Java - and yet, the code is instantly recognizable to any K programmer, needs no documentation about how it deals with NaNs or an empty vector or whatever (which your named C/Java/K routine would - does it return 0? NaN? Raise an exception? Segfault?) And ,// (yes - that’s comma slash slash) flattens a recursive list. Way shorter than its name , and it’s the entire implementation. Are these the most numerically stable / efficient ways to average or flatten? No. But they are the least-cognitive-load, fastest-to-grasp-and-pattern-match when reading code. Once you are used to them. The appeal of Iverson languages also comes from a good selection of primitives. Most modern languages such as C++, Python, Nim, even Rust have an implicit focus on the “meta” programming: they give you the tools (templates, macros, classes) to build abstractions, with which you later build your actual computation. K / J / APL / BQN expect you to do the computation with much fewer abstractions - but provide primitives that make that incredibly easy. For example, there is a “grade” primitive which returns a vector that - if used to index your original list - would sort it. Now, say you have a list of student names, and ages, and you wish to sort them - once alphabetically, once by age. In idiomatic C++/Python etc, you’d have a “student” class with three fields. Then you’d write some comparator functions to pass to your sort routine. (I am aware of accessors and the key arg to pythons sort; assume for a second they aren’t there). In K/APL/J, you’d just have 3 lists whose indices correspond: and then it is just: name[<age]
Read “name indexed by grading of ages”. They’re a terser version: name@<age read: “name at grade of age”The terseness compounds. Once you are used to it, every other programming language seems so uselessly bloated. None of these things apply to obfuscated or shortened C. Arthur released K source code, which is C written in the same style. It does not have the same appeal. |
For example, the NumPy equivalents of your examples are not materially longer than their APL/J equivalents, but are easily readable even by people unfamiliar with NumPy:
> the average of a vector x; or the average of each column in a 2D matrix x
or, to use your example verbatim, > flattens a recursive list > name indexed by grading of ages Though for this application, you’d probably be using a dataframe library like Pandas, in which case this would be