BQN is great, but I rarely get to use it for anything... useful. I hope the ecosystem and tooling around bqn will get better in the future. I would like to see something like what dyalog is to apl for bqn. A jupyter notebook style ide+repl (maybe a bqn for of RIDE), libraries for common scripting tasks, embedding (a la lua) in more languages, maybe even a way to compile to libs so you can call bqn code from other languages. I'd definitely use it more if it was open to more use cases.
Have some experience with J, would like to comment.
> A jupyter notebook style ide+repl (maybe a bqn for of RIDE)
J terminal looks pretty close.
> libraries for common scripting tasks
There are phrases (see www.jsoftware.com), but for many tasks it seems not many libraries are actually needed.
> embedding (a la lua) in more languages, maybe even a way to compile to libs so you can call bqn code from other languages
There is a way to call C from J and vice versa; not too convenient maybe... but still quite possible. APL languages don't benefit much from compilation, J interpreter is very fast.
> APL languages don't benefit much from compilation
Sorry, but this is nonsense. APL tends to spend all its time waiting for memory, because it fuses nought. And it benefits as much as anybody else from things like common subexpression elimination and loop-invariant code motion. APL implementations appear to be fast because of a few extenuating factors:
1. C compilers kind of suck, and c kind of sucks for writing fast code, yet it is the de-facto standard, so it is what things are compared to.
2. Developers of apl implementation care about and prioritise performance.
3. In comparison with languages such as python, and in particular their popular implementations, apl spends very little time on dispatch.
What kinds of numbers do you expect here? It's really only arithmetic that's memory-bound, even something like a scan or compress/filter does a fair amount of CPU work and wouldn't benefit that much from fusion (if you even can fuse compress). And with fewer loops I don't think loop optimization is as relevant. I think a factor of 2 is the median kind of improvement I'd expect from a compiled SIMD APL on array programs? Which is a long way from the 10x or so you get by adding a JIT to other dynamic interpreted languages, and likely harder. I would describe that as APL not benefitting much from compilation; maybe you wouldn't.
I think a factor of 2 is quite significant, but I also think you may understate the advantages of compilation. I do agree the benefit is prone to be less than for a language like python.
Regarding scans (filter is a type of scan, howbeit easier to implement than the general case), there is in general an annoying work-span discrepancy, which bodes ill for contemporary computers with their finite parallelism. I would buffer heavily here, but fusing the scan with its input allows for the use of a work-efficient implementation, spending all latent parallelism on generating more inputs.
When I speak of loops, I am including any usage of rank (incl. implicit); in this respect, apl is chock-full of loops. Some loop optimisations may be obviated, of course, because of referential transparency, but others arise in their place. Take for instance some recent work[0] done on futhark: rather than parallelise an already in-place algorithm, they had to in-place an already parallel algorithm!
Two more points:
A problem may be bound by memory latency. I spent some time tuning j's I. recently, and it does many searches in parallel (4 for a small search space, 12 for a large one, iirc), but it is still bound by latency. If I could fetch a pivot corresponding to the first cell of y, and then generate the next cell while I wait, I would make better use of resources.
A compiler results in more transparent performance characteristics. When I target an interpreter, I must write my idioms and special combinations in exactly the way it expects, else nothing will happen; a compiler will care much less about exact phrasing. Similarly, like I mentioned, you get licm and cse for free, rather than needing to do them by hand.
Feels like a pretty idyllic view of compilers? I guess I agree that there's more than a factor of 2 in instruction latency versus throughput, but I don't think a compiler can use the entire gap. For a quick check, I measured 1.82 instructions per cycle to compile ~0.3MB of source in BQN (the compiler's a very array-oriented program for those unaware) and 1.38 ipc on ~1.7MB where caching's worse. The maximum is probably somewhat less than 4 since not all instructions fit 4 to a cycle; on the other hand, BQN primitives are probably already doing some things that are throughput-inefficient to use available parallelism better. Fused compiler output definitely wouldn't include as many loop counter and load/store instructions.
Some of the problems you mention, especially special combinations, can be addressed by a bytecode compiler that ultimately defers to an interpreted VM.
The binary search interleaving seems like exactly the kind of thing that APL interpreters can do to close the gap with compilers. "generate the next cell while I wait" sounds difficult in general: instruction reordering only goes so far, so if the binary search loop needs many iterations, then only part of it can overlap with other code (barring some compiler intervention that I'd categorize as fanciful if the loop length isn't known). That other work could also be polluting the cache.
Your binary search work is definitely interesting; I found the code and will be studying it as this is something I've been wanting to address better in BQN. Mind if I email about this? I implemented a different solution to high latencies for large searches in Dyalog, which is to partition the array y (searched-for values) by a single pivot from x and recurse. See the last paragraph at https://mlochbaum.github.io/BQN/implementation/primitive/sor....
> For a quick check, I measured 1.82 instructions per cycle to compile ~0.3MB of source in BQN (the compiler's a very array-oriented program for those unaware) and 1.38 ipc on ~1.7MB where caching's worse
It's not really clear to me what this is measuring, and whether that thing is interesting. I care about throughput as measured in widgets per unit time, not instructions per unit time; the contribution of a compiler may not only be in instructions per unit time, but also instructions per widget.
> Some of the problems you mention, especially special combinations, can be addressed by a bytecode compiler that ultimately defers to an interpreted VM.
Sure. But aside from implementation effort, that seems monotonically worse than a compiler targeting machine code.
> if the binary search loop needs many iterations, then only part of it can overlap with other code (barring some compiler intervention that I'd categorize as fanciful if the loop length isn't known)
What interventions? FWIW I think the main limitation is (architectural) registers (plus rob/rename, of course), but you can balance around that by putting multiple _dependent_ search iterations, and leaving enough time for them all to finish.
Also: I think it's probably good to have at least a rough idea of how many iterations things are going to take, and am vaguely planning to specialise on order of magnitude of each dimension (plus exact value when small). Knowing how big things are also gives you a fairly good idea of what is going to pollute cache (conservatively assume that random accesses are uniformly distributed) and by how much.
> Mind if I email about this?
Feel free! elronnd@elronnd.net. FWIW I think the gather hammer, where supported, is probably ideal--I benchmarked it, and it lost, but I was on AMD hardware at the time, which is quite abysmal in that respect; intel seems to be much better, especially with avx512--and that leads to much more straightforward code.
> I implemented a different solution to high latencies for large searches in Dyalog, which is to partition the array y (searched-for values) by a single pivot from x and recurse.
I figured it was doing something like that. Was thinking about implementing something similar, but vaguely hoped that if the buffers were large enough and I could keep them filled, it would be possible to avoid the overhead of partitioning and of scattering results. Also the option, depending on relative sizes, of preprocessing x, producing e.g. a b-tree or eytzinger. Fairly low priority, so I've not yet looked too closely.
The operations on "entire arrays at once" don't benefit much from compilation, because the interpreter overhead is amortized across the size of the array.
For "one potato two potato" scalar code code with lots of branches and control flow, compiled will always be faster because each little op has overhead and a compiler could optimize it away.
A sufficiently smart interpreter or JIT could theoretically optimize this away too, but as far as I know no APL (or APL-like) language uses anything like a tracing JIT, instead they have historically focused on optimizing idiomatic expressions so "typical" code is faster.
There is a lot of development happening in the area of APL-insipred programming languages.
I've spent far too much time working on an APL dialect that allows you to combine APL with imperative structures at the same time. I really need to document it better though. https://aplwiki.com/wiki/KAP
Then there is April, which is a very neat version of APL that is implemented in Common Lisp. It allows you to miss Lisp arrays with APL arrays, giving you the best of both worlds. It's very functional even now: https://github.com/phantomics/april
And of course, BQN is a new language that takes a lot of the good ideas from APL but also changes a lot of the symbols. It's a very nice language: https://mlochbaum.github.io/BQN/
> What pushed the development of April really is that April is used by a hardware startup called Bloxl (of which I am the CTO). There are other users but Bloxl is the flagship application.
There was a paper in 1993 about "APROL: a Hybrid Language" that combines an array processing language and a dialect of Lisp (Scheme) in a consistent and useful manner. Seems like April is much further ahead!
The only time I see APL is each year during Advent of Code – https://adventofcode.com/ – APL users always pop up and manage to solve the most complex of tasks with about ten symbols versus everyone else's 50 lines of Python :-)
I tried to do AoC in sed one year. Didn't get all the way through. Real turing tarpit sed is. My solutions had the readability of APL and the brevity Java.
This is what got me and my friend into APL-likes. My friend even made an APL-like towards the end. I assert that APL is one of the main, definitional pillars of programming languages, the way Lisp and C and Prolog are. It is extraordinarily easy to build an APL with scalar and vector support -- once you start thinking about higher-dimensional data structures, though, it is the ergonomics and the design that become subtle and tricky. (Ivy, in particular, does some questionable things at with multi-dimensional matrices.) Also, if you think of APL operators as verbs, perhaps there should be adverbs that lift the verbs up into higher dimensions...
APL is from an era where impressively functional code that looks like line noise (another obsolete concept) was considered heroic. Other examples of such code are text editing and text formatting macro languages. Recall that EMACS stands for "Editor Macros" because that was its original implementation.
Back then, computers were so limited relative to human capability that this was still a good thing. Those days have gone. The bizarre syntax of APL has had its day. Its powerful array manipulation primitives can easily be brought forward into a language with modern syntax, and haven't they? In my company there is a lot of Matlab usage but I'm personally not knowledgeable.
> Its powerful array manipulation primitives can easily be brought forward into a language with modern syntax, and haven't they?
Kind of. The languages that one might expect to do this (Julia, R, NumPy) have picked out a few things from APL in an inconsistent way, leaving a lot as well. For example most are missing the generalization of prefix sum that APL calls "scan". So in [0], Conor was able to translate some code to every language in the APL family but not yet any outside of it. Another one, I don't think I've ever seen Replicate[1] outside the APL family. It's a generalization of filter to take an arbitrary count instead of 0 or 1 that's often useful if you know about it.
Julia spells "scan" as "accumulate". I don't think we have something exactly like replicate out of the box, but it's a one-line function definition away:
I agree with the spirit of your point though. Having operations like this as part of your 'alphabet' can be a really powerful thing. That's why I think APL is really well suited to being a domain specific language embedded in a language like julia. Shashi took a stab at this a few years ago, but it'd be nice to revive it: https://github.com/shashi/APL.jl
Ah, thank you! I'd tried to find this for implementing BQN in NumPy (never finished) and didn't. Maybe I thought it only worked with a scalar for the number of repetitions.
I can't make head or tails of the first links, so I'll leave experts to it. The second one, yeah, it's nice syntaxical sugar, but it can be replicated very easily (map + "constant list" + concatenate). And versions of it exist in many languages - even C# has SelectMany https://learn.microsoft.com/en-us/dotnet/api/system.linq.enu...
All the good APL functions can be implemented easily (but please, they're just built-in functions, not syntactic sugar!). It's the selection of which functions to implement that makes it so much better for working with arrays.
"even C#" seems to imply you have less obscure examples than a function in a Linq package, is this the case? I admit I've never looked into Linq but I do see it come up in array language discussions sometimes. It's possible it has a more APL-like philosophy for a slightly different use case. The other difference is that APL uses a function on two arrays, which is nice because you can easily filter based on, say, the previous element.
EDIT: No, the C# function seems to only do the mapping and concatenation and doesn't even take a number of copies, that's really not the same.
APL derivatives (like q) seem to have a niche use in the finance industry for processing time series data. I've played around with J (another APL derivative) and it was interesting. I could see it being useful for certain types of problems. Probably hard to use it on a large team though since it's so hard to read.
In R I often use the data.table library and the style of programming that encourages feels similar to how J made me think.
I'm not from this era and I love APL to bits. My brain is wired for extreme concision. Enterprise Java damaged my brain while APL and similar revives it.
The more thoughts you can fit on a page, the less taxing it is for your working memory.
We try to use abstraction - libraries, DSLs, even lowly functions - to tame ever more complex problems, but anyone who's had to debug a dependency of a dependency is painfully aware of the cost of leaky abstractions.
I had a problem in 2000-s when we needed to put some code into data files, and that code had to be short. So, when space was at premium, APL approach was rather convenient - even though it was already time of Skype and some video over the Internet.
Suppose you have a couple graphs, represented by adjacency matrices (A and B) whose cells represent travel times or distances or some other notion of cost. Assuming their dimensions are compatible, and that the columns of A correspond to the same nodes as the rows of B, then
A ⌊.+ B
will produce a new adjacency matrix (call it C) where each c_ij is the minimum traversal cost from node i to node j. For example, if A represented driving times from all cities in NY state to all airports in NY, and B represented direct flight times from all NY airports to all CA airports, then our result would be the minimum travel time (ignoring parking and security) between any NY town and any CA airport.
The sequence `⌊.+` is an inner product chosen specifically to achieve this effect, with addition where multiplication would normally be, and ⌊ (min) where multiplication would normally be.
Notice that it took me longer to give this under-caffeinated explanation of what is going on than to write the code.
Yes, multiplying graph adjacency matrices over a min-tropical semiring produces shortest paths. APL supports this syntactically, but how does it function in the real world? What if the matrices are extremely sparse, like almost all real world graphs are? Syntax is the easy the problem. The hard problem is having very sparse graphs with billions of nodes and many billions of edges. How is a square matrix going to deal with that? Sparse and hypersparse graph computing is hard, which is not syntactically relevant.
Python does not have this terse a syntax, but it gets very close with multiple bindings to the SuiteSparse GraphBLAS Hypersparse graph processing libraries, which intrinsically supports sparse graphs and a very large number of operators, including the tropical min/max ones. Suitesparse doesn't provide syntax (that's the easy part), it provides the hard part, sparse and hypersparse data structures and very complex dynamic, parallel graph processing optimizations including JIT compilation of GPU accelerated operations.
Here's a notebook that shows your shortest path example using Python and the GraphBLAS. While it's a trivial example, it can scale the the largest graphs possible to use today using SuiteSparse:
NB: I should clarify I'm not advocating for Python's syntax in particular vs APL, I'm saying that syntax is not the hard part of any particular problem, especially when it comes to sparse graphs. There are also other binding to GraphBLAS like Julia that are quite nice. I do appreciate that APL does elgantly permit the notion of graph operations with linear algebra.
Sorry but to me this looks like a cool language to play code golf, but a terrible language to use in production.
It feels like obfuscating your own source code.
> The sequence `⌊.+` is an inner product chosen specifically to achieve this effect, with addition where multiplication would normally be, and ⌊ (min) where multiplication would normally be.
Sorry, it is early for me too. I am wondering if there is a mistake here. Do you mean that addition is where multiplication would be and min is where addition would be?
Hm wait floyd-warshall is subtly different from regular matrix multiplication with (+, *) replaced with (min, +).
We're accumulating into the existing matrix so the order of loop iteration matters, it has to be {k, i, j}. As such I'm not sure how
C_ij := \min_k (A_ik + B_kj)
would work to give all pairs shortest path, since in floyd-warshal the results of C[i][j] at iteration k depend on C[i*][j*] for k-1 being computed, which is not true for the above where C_ij can be computed independently of all others.
It seems like it _is_ possible to easily express APSP via an inner product with (min, +), but it's less efficient than floyd warshal. Whereas floyd warshall asks "What's the shortest path from i to j using vertices in {1..k}", if you instead ask "what's the shortest path from i to j of length at most k" and then use repeated-squaring you can get the answer in n^3 lg(n). See
Allow me to disagree. The page presents toy examples, and those are already unreadable. APL lacks data structures, modules, exception/error handling and typing. Well, since almost everything is a matrix of floats, perhaps typing isn't needed, but that idiom just deteriorates readability. You might think of it as a "bc" on steroids, but a rebirth for APL is uncalled for.
Multi-dimensional arrays provide all sorts of data structures, but modern APLs have also have namespaces/dictionaries, classes with inheritance, etc. Oh, and APL has had exception/error handling since the ’60s, with today’s APLs having rather flexible systems for this.
You can get everything there is in APL, plus no weird symbols in the J programming language.
> learning all the alien symbols is a one-time investment
No one time investment is needed if you want to learn J.
And yes, it does teach you to think in a new way. I am not kidding.
You can see the plethora of (mostly free) books available at the website [0].
Learning J has given me "enlightenment" at the same level The Little Schemer has given me.
(I don't recommend APL to anyone as you need to learn and painstakingly slowly insert those symbols. If you want to learn array thinking, go straight to J. Why waste time and headspace with APL symbols?)
> No one time investment is needed if you want to learn J.
I don't understand the "{J,K,Q} don't require you to learn new symbols" argument. You're learning a mapping between symbols and functionality, not literally learning to recognize the lines and curves that make up the glyph.
"reverse" is `|.` in J and `⌽` in APL. It doesn't seem obvious that the J mapping is easier just because I've seen those characters in other contexts.
It does take a little work to learn where they live on the keyboard, but that doesn't seem like a huge barrier to overcome. The live editor on the BQN site shows a list of glyphs and hovering shows you where they are on the keyboard. You just prefix with `\` to type them, so you don't even need any custom keymap.
Well-chosen symbols aid thinking about the concepts. That's why mathematicians don't write their formulas in ASCII. Once you learn the keyboard layout, it is faster to type APL than J's two-glyphs and three-glyphs.
Ken Iverson invented J after he worked on APL and according to him J is an improvement upon APL.
Most people don't know about J, but know about APL because of those weird symbols.
I agree with your thoughts that not everything should be ASCII (and this is what many comments on this thread don't see), but don't reach the same conclusion as you do.
The core language of J was an improvement on the APL back then, but modern APLs have since borrowed many features from J. Iverson's choice of ASCII was because he wanted to make it more accessible to schools that couldn't get the equipment necessary to display APL glyphs. Note that J was released the year before Unicode.
> a concession to the state of technology at the time.
Well, not anymore. But I will prefer ASCII over glyphs any day.
I have learned symbols in Math, and I keep learning newer ones.
It finally comes down to _RoI_.
The vast and tremendous return I get from non-ASCII characters in Math (and have gotten for decades)- I will not get the same amount from APL. It's as simple as that to me.
I tried to learn it multiple times, but something became very obvious to me when I used it in parallel to Q and read J for C progammers : J doesn't have a nice way to mimic keeping states between loops.
The J for C Progammers author divides loops in, I believe, 7 use cases and for that one he ends up using "an adverb" (a high order function) he wrote himself? Why? Because the language default adverbs don't support it.
In comparison, Q had very good support for that. Q also doesn't require learning symbols.
I haven't tried Q, and didn't read the book that you mention.
I learned J the same reason I learned Scheme- to expand my mind, and perspectives.
And I definitely would not want to use J for solving real-world problems, but I would definitely state that there are a lot of ideas in J-like languages that merit being looked into and being taken from there into modern Deep Learning frameworks.
I intend to learn an APL at some point. I'm an avid listener of the Array Cast podcast [0]. Can't say I u derstand all of what they talk about but I definitely feel like APL has its place for number crunching.
APL looks like a lot of fun, and I've tried to get into learning it for a while, but it's a shame that the most popular implementation is proprietary, and GNU APL is a dialect which is not as featureful and a separate dialect of APL unto itself, so it's not as though you can write a reasonably sized APL program with Dyalog and then expect it to work in GNU APL.
"The GNU APL interpreter is an (almost) complete implementation of ISO standard 13751 aka. Programming Language APL, Extended." [1]
GNU APL is closely following the APL2 standard [2], Dyalog has lots of (proprietary) extensions.
APL does not have its roots in the FOSS world, all the more the efforts to provide and maintain a powerful, free option here are to be supported. It is not self-evident that there are always free software alternatives available.
The standard for APL2 is quite old and doesn't reflect the state of commercial APL at all (Dyalog is the only one with significant development now but APLX and APL+Win kept it going through the 90s). For example, GNU doesn't have control structures so you are expected to program with the branch statement, APL's version of go-to. It also has various extensions of its own, including some that are based on Dyalog but not compatible. It's the only dialect that I would recommend against; I believe April, NARS2000, and dzaima/APL are all better despite various issues.
I highly recommend trying BQN [0]. It's a lot of fun indeed. It retains the array programming paradigm but modernizes a lot of things -- and has excellent documentation (but not much in terms of ecosystem, yet). The main implementation is GPLv3.
KDB+, using Q, is heavily inspired by APL. Great to look smart, might make you productive, horrible for cooperation, maintenance nightmares, heavy technical debt.
Lots of respect for Arthur, KDB+ was transformative when it arrived but I think now we need to move beyond that. Will take one or two decades for banks to migrate though lol.
Hi Shin - KX employee here (the creators of q/kdb+). We launched a new product to take our time-series tech to new markets and usecases with high performance analytic needs. Over the past 2-3 years we've listened closely to the community and have made huge improvements to make kdb+ easier to develop with using options like ANSI-SQL/Python/Stream Processing DSL/microservices/Open API etc. The banks are adopting all of the new kit quickly and leverage the cloud native capabilities (like kubernetes operators/object storage) to help scale and take their deployments to new heights without deep technical kdb+ expertise. Thought it was worthwhile calling out the adoption and product strategy here at KX.. lots more to come!
It's also a misunderstood product from the outside.
Unfortunately banks had a tendency to pay a bunch of senior engineers to do a build out for 3 years and then turn it over to cheap nearshore consultants to operate for 10 years. The core of most big bank code you see was written before 2010.
Where I have seen bakeoffs and largely failed attempts to replace, the challenge is the number of vendor & open solutions you need to cobble together to replace all the use cases that KDB supports. Every year it was a slightly different combination that didn't really cover all the bases.
To replace KDB in many of these large use cases, you need - a fast columnar PB-scale database, an efficient on-disk store, a fast in-memory cache, SQL support plus with expressive query language additions for time series operations, an event bus, a stream processor, and a programming language to write more complex applications close to the data.
A lot of the newer cloud-centric offerings are more about massive parallelism/scale than about pure single thread/individual request-response performance.
This is great for the vast majority of CRUD apps with millions/billions of users.
It's even great for some financial "backtesting" type use cases where you can kick off large
It generally isn't sufficient for many of the real-time financial use cases of doing ad-hoc analytics on billions/day datasets with expected responses in ms.
The biggest complaints are its expensive, and the devs are expensive.
On the other hand you usually don't need as much hardware or dev staff to support it.
> a fast columnar PB-scale database, an efficient on-disk store, a fast in-memory cache, SQL support plus with expressive query language additions for time series operations
Sounds like ClickHouse.
And we see kdb slowly start being replaced...
Hi Steve. See my reply in above thread to Shin which calls out alot of the operational improvements we've made. Just an FYI, there's also been significant changes in how we price so it's now alot easier to start small, get started quickly and decrease the time to value with more mainstream skillsets. With the new product strategy we're seeing alot more buy side clients (hedge funds/asset managers) use kdb+/q for their workloads with KX Insights.
I think you might prefer notation y = a * x^2 + b * x + c to notation where those things are described some other ways. Same here, APL languages ask you to learn some notation - and then effectively use it, so the programs with that could be "meant to be read by humans".
If APL actually followed mathematical notation it would be extremely verbose. Most math books, articles and explanations use quite a lot of words, far more than symbols, precisely to allow them to be “read by humans”. Those are mostly used for computations and symbolic manipulations.
Nobody holds you from adding as much comments to APL code as you wish. I once wanted to write a parser generator in J, a very incomplete version is here - https://code.jsoftware.com/wiki/User:Alex_Mikhailov/Parsing . This is practically text, as you'd expect from a math article, with embedded code which allows computer execution to have those ideas working.
But there is a difference between “verbose code” and “adding comments”, isn’t it? There’s always the issue with comment-code disagreement. But mainly the point is about the language itself and the prioritization of terseness over unambiguity and intuition. Good mathematical notation strives for the latter, not the former.
No, I don’t think APL is a good mathematical notation. For starters, symbols having different meanings depending on whether they are dyadic or monadic is fairly confusing. That’s made worse by the lack of clear delimitations of arguments of functions. The use of prefix notation is also a bad choice because it doesn’t match the way we talk about a lot of operators. Symbols are overused even more than in mathematics (for example, why use a symbol instead of “log”?). There’s also a lot of symbols that are very similar, differing in small ways even though their meaning is fairly different.
Yes, mathematical notation is terse (mostly to make manipulations and computations easier to type) but people take special care to avoid ambiguity or too much context dependency, and also don’t try to introduce symbols for everything.
I've dabbled in APL, and a couple of jobs ago I worked extensively in kdb/q. I found it to be an immensely powerful tool in its natural environment of time-series processing, and extraordinarily painful to use outside that domain. Typically I'd write the core logic in q with a small wrapper in Java or Python to do string formatting and other tasks that kdb falls down on.
Which is fine. Not every language needs to do everything. Horses for courses.
I think it's crazy that we limit programming languages to using ASCII in 2022. Yes, I understand the chicken-and-egg issues of input, but it still seems like we're limiting ourselves as developers if we don't make use of a richer symbology. I know Raku supports some Unicode operators and Guy Steele's Fortress language also supported non-ASCII symbols so it is possible. But I'm not holding my breath. This is just one old man's crazy opinion.
AFAIK Fortress did not actually support non-ASCII symbols in the code. What the Fortress team did is have a display layer that was fed a list of symbols using ‘\BigSum’ style ASCII and then merely displayed a selected Unicode symbol in the editor.
"Wouldn't it be nice if modern programmers had some more symbols at their disposal?"
Emacs lets you display arbitrary text as any symbol you want, and you can also map your keyboard to generate that text with any keystroke of your choice.
So the result is the same, without needing language support for the symbols you want to use.
I've done this with the lamba symbol and symbolic logic symbols when editing LaTeX files in emacs. It works great.
Read up on the Compose key. ASCII-based keyboards also don’t not prevent Chinese/Japanese/Koreans/etc. from writing their scripts with them. Entering Unicode symbols by keyboard is not a problem. Few programming languages support them for operators though.
I’ve been dabbling in this, and created my own keyboard layout to include all kinds of math characters using the right Alt key for level 3 and level 4 shift. Also replaced caps lock with the compose key. Didn’t take too long to get accustomed to it since it’s a corruption of the Greek keyboard layout (which is laid out similar to QWERTY).
I haven’t had the audacity to use it in production code, but it’s interesting to play around with and great for quick note taking or hacker news comments. (Sadly I’m on my phone right now to not make use of it for this comment)
I am using a Stream Deck for that. Should have bought the largest one, but even the medium size on is quite useful via nested menus. Here are some symbols I am using for a formalisation in Isabelle currently: ∀∃⊤⊥𝔇𝔄≽⇒𝒰υτ↣𝟭‹›∅×∈⊆
Something like the DuckyPad[0,1] might work for that as well. I haven't tried using mine for APL or anything like that, but it's been great for use as a "Zoom meeting control", numpad, and MacOS shortcut pad.
I like Ivy's[0] approach by using keywords instead of symbols. I feel like something like APL should either be embedded in some host language, be given lots of functionality for I/O, or some static type system with rank polymorphism.
I agree!! I think having APL be a embedded domain-specific language is the perfect fit. I don't want to give up record and union types when I'm programming something large; but some piece of the app I'm writing will want to perform vector math or compute order statistics, and for that there's nothing better than APL. Note that Python and numpy is shockingly close to this state of the world already: Python's the general language, and numpy is the beautiful vector escape hatch. If only Python and numpy were statically typed.
Well, it won't happen. The language is too weird and for a language these days to make it, it has to target the lowest common denominator of developers.
Let's say you're at work, and you casually mention to your team something about APL, ATS or Agda. How many were excited about it? How many did roll their eyes? Yeah, I thought so.
There are a lot of comments of the type "it's unreadable/write-only/unmaintainable" here. It's a natural reaction; I know, I was there. It looks different. But readability is in the eyes of the beholder, not the language. I made this point in my APL book (https://xpqz.github.io/learnapl) -- just because I can't read Japanese does not make Japanese unreadable. Since I wrote that book, I've spent a few years immersing myself in APL, gradually rewriting thousands of lines of Python into APL, and it's such a productivity boost. My code is an order of magnitude smaller. More, sometimes. Fast, too.
For the right use cases, it's unbeatable. Sure, you probably wouldn't want to write an OS kernel in it, but anything that reads a bunch of data, mashes it up, and spits out some result, APL is a hand-in-glove fit. And with modern SIMD processors, APL really screams.
Recently someone posted Decker here on HN, a HyperCard-inspired environment. It includes its own programming language, Lil, which looks like it took some APL influences but made the core design more based on Lua. Meaning the language hopefully feels familiar enough avoid scaring people off.
I wonder if something that might be a way forward, similar to how lots of imperative languages now have language constructs that makes it easier to do functional programming in them: steal enough from the array languages to make other languages less bad. Or maybe that's what NumPy and Julia already are, which also would show the limitations of that approach. I dunno, I've read about array languages out of theoretical interest but never actually programmed in them.
I'm extremely confident that array language features will seep into the mainstream in the same way that functional programming features have been doing for the last 10 years or so.
New language designers will have to defend why they don't have array language features rather than why they do.
Unfortunately, given that the program is going to be read by people, what we care is about what the beholders say.
> just because I can't read Japanese does not make Japanese unreadable
A nice comparison: Japanese is indeed harder to read than other non-symbolic languages. Symbols are harder to recognize because there are far more of them than letters in alphabets (latin, greek, cyrillic). For example, do you think it's easy to recognize the difference between 陳 and 陣? The more symbols you have, the harder they are to process, be it Japanese or APL.
Yet millions of Japanese people are fairly adept at reading it. To be clear, APL actually has a tiny set of symbols. In day to day use, perhaps two dozen. Remember that these aren't "keywords" in the traditional sense, but more like the standard library. Contrary to popular belief, it's actually pretty easy to learn to read, as usually the symbols are mnemonic, or referencing well-known mathematical counterparts. Flipping a matrix in various ways is a circle and a line. For example, ⍉ means "flip diagonally" -- in other words, a transpose. To flip around the horizontal axis, guess the symbol: ⊖. Around the vertical? ⌽. The ceiling of a number is borrowed from maths: ⌈ etc. Counting the elements in an array is ≢ -- evoking the symbols you scratch on a piece of paper when tallying something up.
> Yet millions of Japanese people are fairly adept at reading it.
Yes, because it's their native language. Doesn't say anything about the difficulty of it. My mothertongue is Spanish and I'm fairly adept at using it, and still doesn't mean that some aspects make it harder than other languages (verb conjugation, gendered nouns).
> or referencing well-known mathematical counterparts.
As a mathematician, other than the very basic symbols, most of the ones used in APL are not familiar, or are used in different contexts. For example, × is cross product and in APL is regular multiplication, ⍟ is logarithm even when "log" is what's used in math, and "⊥" looks to be polynomial evaluation when that's usually the symbol reserved for orthogonality.
> Counting the elements in an array is ≢
Funnily enough, that symbol is "not equivalent" or “not congruent” in math. Never would have though it refers to the symbols in a paper.
When used with 2 arguments, e.g. `a ≢ b`, it indeed is called "not match", i.e. are `a` and `b` the same. When used with 1 argument, `≢ a` it will give you the number of elements.
Yeah, the fact that the same symbol is overloaded with two meanings for the one-arg and two-arg (infix) cases adds some overhead to reading APL code. The precedence rules are very simple and uniform in isolation, but personally I find that applying them to (mentally) parse a nontrivial line of code is one of the hardest parts of getting started with APL.
Reading information rates are pretty similar across major written languages. (1.42 ± 0.13 texts/min). Ideographs take longer to recognize but contain more information.
Japanese readers do indeed take in information slightly more slowly than average, but not as slowly as Finnish readers -- a language with 26 latin-based characters.
> Ideographs take longer to recognize but contain more information.
I think this is a huge part of what makes it hard for people. You have to read denser code slower. APL code can easily have 1/10th of the characters when compared to code in popular languages. If you read at the same speed then you're reading... ten times as fast. That's a bit much to ask. You could read a five times slower and you're still covering information at twice the speed.
Wonder if is there a sweet spot, sort of the language equivalent to what ternary is to number systems, the integer base with best theoretical radix economy (number length vs. Maximum different states coded)
> Japanese is indeed harder to read than other non-symbolic languages. Symbols are harder to recognize because there are far more of them than letters in alphabets (latin, greek, cyrillic).
Do you have a study that confirms that kanji are harder to read for Japanese speakers than romanized transcriptions?
In English or Spanish, we also don’t read individual letters to reconstruct the words that they represent. We read the shape of each word. This is why “floor” and “flour” are harder to tell apart than “floor” and “flower.”
I don’t have the studies at hand but I don’t think it’s such a difficult notion to accept. For starters, there are more symbols than letters, which means you have more things to learn.
But the important thing is how symbols/words are processed. For any given concept, we might have the symbolic representation, the auditory representation and/or the pictoric representation. The good thing about words is that, even if you haven’t seen the written word before, it maps to the auditory representation (more or less depending on the language). Kanji doesn’t (in fact, the same symbol might map to different “words” and sounds) and it’s not a very precise pictoric representation either.
This is not to say that Japanese is impossible to learn or much more inefficient. Just that it makes it harder to learn and read. In a similar way other languages have traits that make them easier or harder. For example, English is harder because of the inconsistent mapping between writing and pronunciation. French is harder than other languages because of the number of vowel sounds and how important it is to make them different. Spanish is harder because of gendered nouns and the fairly complicated conjugation norms for verbs. Of course native people get used to those things, still doesn’t mean they don’t make things harder or easier.
It is true that learning to read the kanji is harder than learning to read, e.g., Spanish. But I understood your original statement to not be about ease of learning but ease of reading (once learned). It is not obvious to me that Japanese is at a disadvantage here when we compare kanji vs. romaji within the same subject.
By the way, you might know this, but if a Japanese speaker doesn’t know a kanji, they are not at a complete loss. Words often consist of multiple kanji so they can infer from the meaning and possible pronunciations of the other kanji in the word. The context in which the unknown kanji appears helps too. And in literature aimed at young readers, they often also show the pronunciation written next to more unusual kanji (furigana). All of this is less relevant to APL though.
Usually hard to learn and hard to use are very correlated concepts. If something is hard to learn, it usually requires more mental energy to read. For an example closer to APL, mathematical notation is harder to read than the verbose equivalents. You not only need to keep the concepts in mind but also how the symbols are translated to the concepts. Maybe when you’re very familiarized with them they’re as easy and a little bit more terse, but it’s easy to slip back if you’re tired or just woke up less bright that day.
And that’s just in math, where there isn’t as much symbol density as in APL and where symbols don’t depend on context too much (it’s usually not a good practice and avoided if possible to have the same symbol having very different meanings depending on the context).
I just counted how many distinct symbols are in five projects I've been working on lately. They use 30–50 primitives each. Across all of them, it is 56.
"Fast" is not relevant - the runtime would be just as fast if the code was written in plain words. Code size is also irrelevant unless you're called Elon Musk. The only claim left is that it's a productivity boost, but I just can't see how a language that needs a special keyboard to be written can be written more productively than actual words, especially with modern IDEs.
Speaking for myself, I find that whenever I think "I just can't see how..." it makes me want to figure out what it is that others can see that I can't. This is the reason I learnt APL, actually, after seeing half a page of k code and feeling annoyed that I couldn't read it. k subsequently led me to APL.
You can try it for yourself -- NumPy is a fair "Iverson ghost" -- APL without the symbols: it has a similar enough array model, and most of the APL primitives as functions or methods. APL lets you express linear algebra in a very natural way. Doing it in NumPy is much more convoluted.
The claim isn't that writing is faster, the claim is that there's less pointless boilerplate to write, read, or think about. You didn't write your comment as:
"quote fast unquote is not relevant hyphen the runtime would be just as fast if the code was written in plain words period code size is also irrelevant unless you apostrophe are called initial capital elon musk period the only claim left is that it apostrophe s a productivity boost comma but i just can apostrophe t see how a language that needs a special keyboard to be written can be written more productively than actual words comma especially with modern initialism ide s period"
because symbols are useful. And people don't have a problem with peppering code with chorded symbols like () ++ {} : <> ? without demanding that they be turned into words because words are easier. In fact people struggle when making new languages (Rust, C# new versions) to find more symbol combinations, reaching for things like Python's triple quote strings """ """ and :: and ""u8 and so on.
There's nothing inherently better about shift-2 shift-2 shift-2 shift-2 shift-2 shift-2 than altgr+i
Why aren't the common operations of transforming collections short, sharp, and getting out of your way? Why are they ceremonious "map a lambda which takes a parameter with a name and ..." when you don't need that.
You don't worry about repetitive strain injury from all that chording? I try to avoid that sort of thing as much as possible; even though I use emacs, I do it with evil bindings. Capslock rebound as control is useful, but the bottom row modifiers seem problematic. I can't easily reach those modifier keys while also keeping my fingers on the home row; I have to either contort my thumb or pinky in a bad way, or move my entire hand (which is usually how I use those keys.) Either way, keeping hands straight and my fingers positioned on the home row seems much safer and comfortable. I just can't imagine using a language where I have to altgr for every character I type.
Hasn't really been an issue. Occasionally, I map CapsLock to AltGr so I have a left-side APL shifting key too — it doesn't see much use, though. Also remember that it isn't "every character I type". Actual APL primitives only comprise a relatively small fraction of the total code. I just did a rough computation on four things I've been working on recently and they had 4%, 5%, 6%, and 7% non-ASCII APL glyphs, respectively.
What about using gesture recognition on a smartphone or tablet? It could become a useful portable calculating/coding app that doesn't need much typing. I know there are J apps and web APLs available, but not gesture ones that I know of.
Came here to say exactly this -- an APL-like notation + programming environment would seem to be a great way to program on a tablet + stylus. I'd love a notebook environment where I can mix equations, doodles, executable code, & their output.
That's a cute writeup, but the author overlooked some important bits of history.
For example, the original use of APL (before it was called APL and before anyone had implemented it as a programming language -- the reason Iverson joined IBM) was to specify the IBM 370 machine architecture.
In other words, it was being used to document how the CPU worked. And it was impressively successful there, distilling a large body of documentation into 2 pages.
This reduction in cognitive load, describing machine structure, is what made it popular back then. And this is what motivated the effort to implement it as a programming language.
(Also, if I understand correctly, there's also a relatively short and direct step from APL to the initial implementation of SQL.)
The original use was to "to implement the world's first graduate program in "automatic data processing"" at Harvard; Fred Brookes volunteered to join in and "It was in this period that Iverson developed notation for describing and analyzing various topics in data processing, for teaching classes, and for writing (with Brooks) Automatic Data Processing. He was "appalled" to find that conventional mathematical notation failed to fill his needs, and began work on extensions to the notation that were more suitable."
Then "Iverson joined IBM Research in 1960 [...] he was allowed to finish and publish A Programming Language and (with Brooks) Automatic Data Processing, two books that described and used the notation developed at Harvard. [...] At IBM, Iverson soon met Adin Falkoff [...] Chapter 2 of A Programming Language used Iverson's notation to describe the IBM 7090 computer. In early 1963 Falkoff [...] proceeded to use the notation to produce a formal description of the IBM System/360 computer then under design."
A lot of people in finance like to us q, an APL derivative with some more functional programming facilities, so in that sense it's had a mini-renaissance.
I had to use APL once back in the 1970's, because I needed the graphics capabilities of an IBM 5100. I managed, but it wasn't a pleasant experience and I've felt no compulsion to repeat it.
I admit it was kind of cool to be able to operate on whole arrays at a time, but nowadays I can do that with Python's numpy.
I particularly liked this principle from the article: "learning all the alien symbols is a one-time investment, and expressiveness — the leverage you as a programmer gain — is for life."
I used APL professionally for about ten years (early '80's to early '90's) for a range of applications spanning various business applications, industrial automation and DNA sequence analysis used in the Human Genome Project. The language is/was fantastic. It truly is a tool for thought.
How about the funny symbols?
I see this type of comment all the time. If you think this way, you lack context. The notation is an important element of APL's value proposition. You cannot understand this by watching an APL video on YouTube or running through a tutorial.
I equate it to something like using vim. People who have casual contact with it absolutely hate it. Those who commit and develop the skills and mental automation have a very different perspective. From that context, someone saying "vim's cryptic keyboard commands and states are horrible" sounds, well, I'll be kind, uninformed.
And yet, my first sentence implies I think APL does not deserve to exist. Which is it?
I firmly believe the future of advanced types of software engineering will require a form of notation in order to effectively communicate and describe computational solutions. What comes to mind is AI/ML. I think APL needs to mutate and evolve into being the tool that is best-suited for solving complex AI/ML problems. If this can happen, the notation will be critical and just as important as it is in mathematics or music.
I think the issue might be that we don't yet know enough about this evolutionary stage of AI/ML to understand what kind of programming notation we might need to invent. This isn't well defined at all. For the most part, AI/ML is exactly where it was in the 80's and 90's. We have faster computers with massive resources. Yet, if you go back to books on AI dating back to the 80's you will be surprised to learn we haven't really invented much in the last few decades.
This three volume set on my bookshelf is a good reference dating back to the early '80's:
In fact, if you read through it you'll discover just how much was done in the '70's and '60's.
So, yes, I would not use or recommend APL for any modern project. It's a liability from an ROI perspective. In addition to this, the pool of capable APL software engineers is microscopic. That is a huge problem. APL does not make business or technical sense and cannot compete with modern tools, their libraries and the large pool of capable programmers who can use them.
I disagree. The terseness of APL is the worst part about it. Its arcane symbols are a close second.
I don't want my programming language to be as terse as possible. I want it to be easy to read and maintain. I don't see how APL is a step in the right direction at all, and IMO should just be relegated to the dustbin of history as a failed experiment.
APL (and probably many languages in the Iversonian family) are more geared towards expressiveness, although terseness plays a large part of the language design.
Also, once you understand the rules of these languages when parsing expressions (which is always right-to-left unless the case of a parenthesis) and all the symbols, readability become less of a problem.
As for maintainability, well, it kinda depends on you and whoever is going to maintain it.
I do agree that the arcane symbols of APL is a minus, though. Which is why languages like J, K and Q are born: to make APL ASCII-friendly.
Generally speaking, this seems to me like a comment from someone who doesn't give APL more than a cursory look. And if that relates to you, please, try APL, or any other languages in the family, just even once. You _may_ change your view about it.
> Also, once you understand the rules of these languages when parsing expressions (which is always right-to-left unless the case of a parenthesis) and all the symbols, readability become less of a problem.
This is a very limited view about readability. Of course, once you get used to anything, it is more readable. But if most programming languages have moved away from symbols other than the standard ones precisely because symbols are a readability problem (even some languages like Python have and/or/not instead of &&/||/!, and those are pretty standard). It's easier to read "mod(1, 3)" than "1 % 3".
> As for maintainability, well, it kinda depends on you and whoever is going to maintain it.
Again, putting these issues on the people is missing the point of the discussion. Does the language incentivize the creation of easily understandable, fixable functions? Is it easy to detect and fix bugs? For example, assembly is harder to maintain than Python because a bug in assembly is hard to find, possibly complicated to fix because coding in assembly isn't really that easy... on the other hand, a bug in Python probably shows up as an exception even, that tells you in which line of code it's crashing.
> Generally speaking, this seems to me like a comment from someone who doesn't give APL more than a cursory look. And if that relates to you, please, try APL, or any other languages in the family, just even once. You _may_ change your view about it.
Honestly, J/K/Q/APL make it really hard to give them more than a cursory look. And the problem is that I've never seen a really good argument to do more than that. For example, first time I saw Haskell, it was hard to pick up, but at least I saw value in the proposition of immutability, pure functions, etc. Not for all use cases, but at least for some. I haven't given Go more than a cursory look but, again, the idea of a compiled language with GC and memory safety looks cool. I've never seen the value proposition of these family of languages other than "people who like them say they like them", and by the looks of the comments I don't seem to be the only one.
I think the difficulty of this is overstated. My kids are learning to read Armenian and English at the same time. 38 Armenian letters + 26 English ones, each with upper and lowercase versions. If a 6 year old can do it, you can learn a couple of new glyphs when programming.
> Honestly, J/K/Q/APL make it really hard to give them more than a cursory look. And the problem is that I've never seen a really good argument to do more than that.
It's a little disingenuous to claim that something you've never tried to do is too hard and has no value.
> Does the language incentivize the creation of easily understandable, fixable functions?
Lol, that is literally the point of them.
I've been really enjoying https://www.arraycast.com/ in which enthusiasts from several different Iversonian Array languages talk about how array languages enable them to solve problems differently.
I have my degree in mathematics and I've used my fair share of symbols. It's been a consistent finding in articles, books, classes and peer conversations that symbols are not that optimal. Inventing new symbols, or overusing them is frowned upon because they don't really make things easier to understand and can sometimes be a cause of confusion.
> It's a little disingenuous to claim that something you've never tried to do is too hard and has no value.
I'm not saying "it has no value", I'm saying that I've never seen a good argument to actually invest time in them. As I explained, other languages have clear value propositions. In J/K/Q/APL , the value proposition tends to be a vague "I like it" from people who like it.
> Offtopic, but I really dislike how so many information out there is the inaccesible format that is "podcasts".
I can't think of a better format for what they are trying to do with the ArrayCast. It's not teaching people array programming, just a conversation between people who are proficient in it.
What do you think makes podcasts inaccessible? For example, I often recommend https://soundcloud.com/lambda-cast to developers looking to understand functional programming. The conversational approach works well because some of the panelists are being taught these concepts for the first time and they ask great questions. There are plenty of books and articles on the subject, but conversational audio is a wonderful supplement.
But far less than the equivalent code in, say, Python, or C, or any other language. That's the point.
> What do you think makes podcasts inaccessible?
Lack of visible, searchable structure and the requirement of audio (in those that don't have transcripts, which is a lot of them) to get the information.
All episodes of the Arraycast have both transcripts and extensive show notes for background information for those who may not be array programmers. The challenge of delivering knowledge via an audio format is real, but use of transcripts makes it much more accessible.
Indeed, this podcast seems to do a decent job. It was more an off topic remark about how inaccesible podcasts usually are for actually finding information.
2_&{&/x!/:2_!x}'!R is very understandable to someone who knows K. (Or at least, the idea is, do you know what dialect it uses? Where's the code from?)
The (roughly) equivalent python is filter(lambda x:all(x%i>0 for i in range(2,x)),range(2,R)) - is this really that much better? (yes if you know python, no if you know k)
> filter(lambda x:all(x%i>0 for i in range(2,x)),range(2,R)) - is this really that much better?
You don't need to know Python. Filter is probably doing a filter, "lambda x: ... " looks like a lambda function, for looks like a loop... I am 100% sure that, without any language knowledge, the Python version is far more easy to understand.
Ah, that code was added to the wiki page in 2006 apparently. A modern version is basically the same anyway.
I will (maybe) agree that the python is easier to understand without any language knowledge - but that does not mean the K is not understandable. If you know K, it is understandable, and knowing a language seems like a pretty low bar for understanding it.
I mean, by the same measure, spaghetti code is not spaghetti code if it makes sense in your head. But we all understand that there are certain qualities of code that make it “spaghetti” or not independently of your understanding of it. Similarly there are features of languages (such as terseness, use of symbols, dependency on context) that makes them harder to read
If you don’t know a language but are hoping to read it, for what purpose are you intending to read it? Is it a casual understanding? If you’re talking about maintaining the code it’s probably better to stipulate that people already know the language. If you want to maintain code in a language you don’t know, I’m not sure you’d be a great hire.
Unless you have perfect memory and perfect recall, it’s very possible you’ll forget what a certain symbol meant. Specially if you read the code at a time you’re not being specially bright (say, Friday noon after an stressful week). Much harder to forget what the word “filter” means. Also much easier to recognize words than symbols where differences might be small.
It's really unlikely (imo impossible) you'll forget what a symbol means in APL or K etc if you program in it reasonably often. There aren't many symbols, and most of them are used pretty frequently. If you do forget, there's plenty of resources to jog your memory though. The Dyalog RIDE has a language bar at the top, and hovering over a symbol gives a brief description with examples. https://janiczek.github.io/tryapl-elm/ replicates this if you want to see for yourself. I disagree that it's easier to recognise words than symbols if there are small differences, the symbols are distinctive anyway so that's not really a real problem.
> It's really unlikely (imo impossible) you'll forget what a symbol means in APL or K etc if you program in it reasonably often.
Well, of course, if you’re always practicing the same thing you’re unlikely to forget it. That’s not always the case, though.
> the symbols are distinctive anyway so that's not really a real problem.
Really? From that toolbar alone I’m seeing empty circle vs slightly smaller empty circle, a lot of symbols with/without a dash or umlaut, dot and comma… seems pretty easy to get a lot of those confused or misread.
Um, yeah. Knowing a language generally involves putting time into learning and the benefit is not forgetting stuff. You seem to not want to own the fact that your entire impression is predicated on it being not practical for someone not interested in investing any time into it. But I’m not sure that the input from such people is ever valuable.
It seems like it might be easy to confuse things, but from experience, it isn't.
I feel like in general, you're making a lot of (fairly reasonable) assumptions about what programming in an array language is like, but they aren't really the case (as lots of other people have tried to explain). Without really trying it out, I'm not sure how someone could convince you otherwise, so there's a bit of an impasse.
I have listened and to me it always seems to come down to “you make reasonable points but it’s not a problem for me so it’s not a problem”.
And I think there’s a lot of implicit bias when people who like APL say it’s perfectly readable. Given how readability hits you in the face first thing when you see APL and how it’s not a widely used language, only the people enthusiastic about it and willing to overlook the readability issues will end up learning it. Of course they don’t find issues in practice, if they had issues with that they’d have probably abandoned the language quickly.
> and trust that for some people who do learn array languages, they do find it useful + productive, and a lot of the things you've raised are not issues in practice.
I’m not saying it isn’t useful or productive. That’s not the discussion. The point is that they’re languages that they’re hard to use and hard to read. I don’t see how it’s such a controversial point.
For example, I like LaTeX a lot. I’ve done quite a lot of things with it, it’s been very useful and I’ve been very productive with it. I have no problem reading that code. But the fact that I have no problems doesn’t mean there aren’t actual problems with LaTeX. LaTeX will always be hard to read and quite a bit messy no matter how used I get to it.
Yes - the first thing you notice when you see APL is 'this looks weird'. Most people then assume 'this is complicated, hard to read, and hard to use', and move on with their lives. Some people decide to learn it anyway, and quickly (within under a day) realise it is actually simple, readable, and easy to use!
It's not like some people are magically born with the ability to read APL (or anything else), and learning it isn't a matter of 'overlooking the readability issues', but rather realising the 'readability issues' are a false assumption. There's not 'bias', there's just experience.
That is why "they’re hard to use and hard to read" is 'controversial', because it is just not true.
> Some people decide to learn it anyway, and quickly (within under a day) realise it is actually simple, readable, and easy to use!
Do you think all people who decide to learn it anyway end up with the same conclusions?
> That is why "they’re hard to use and hard to read" is 'controversial', because it is just not true.
So things like symbols having different meanings depending on number of arguments, prefix notation making the arguments of functions/operators stand out less, making it reading right-to-left (which is different from what you usually read - that brings the fun question of whether APL is left-to-right in Arabic countries), or having to know extra symbols that aren't used in other contexts... All of those things are not real?
I come back to the LaTeX example because to me that's very similar. I get when you say "I don't find APL hard to read/understand/work with" because I feel the same with LaTeX (even though it's not as obscure and "alien-looking" as APL is). I write slides in LaTeX faster and better than what I could do with Powerpoint. But that doesn't deny that there are a lot of things in LaTeX that make things harder, even when those are part of the core and when removing them would mean removing good things about the language too.
I think all people who try to learn APL for more than about 5 minutes end up realising most of their assumptions were completely wrong. Maybe they don't reach exactly the same conclusions (a lot of people do), but they reach similar ones.
> So things like <APL things> are not real?
They are real, but they are not issues for anyone who has tried APL at all.
Advent of code is starting soon, I'd recommend that you pick an array language and give the first couple of problems a go in it - even if you don't like it, at least it would save you from making yet more uninformed comments next time an array language article is on HN (and save array language programmers worldwide the pain of reading them).
APL is pretty big on immutability and pure functions too. Arrays are immutable, period, and the link below has John Scholes talking about the advantages of declarative programming back in 2005. And, uh, yes, APL tells you where the error is when you get one. It's pretty obvious you've bounced off due to a surface-level dislike for the syntax and are guessing about any other features it may have.
> It's pretty obvious you've bounced off due to a surface-level dislike for the syntax and are guessing about any other features it may have.
I'm not saying APL doesn't have those features. I'm making examples of languages that have a clear value proposition. If APL is big on immutability and pure functions, then why use APL vs, say, Haskell or Clojure? Same with the "where is the error thing", I was explaining why maintenance isn't just a "depends on who maintains it" thing, in fact the example languages were assembly and Python, nothing to do with APL.
I've talked to plenty of people whose eyes light up when you tell them 5 + 1 2 3 just works in the language. What you're saying is that you didn't find APL's value proposition attractive, and this is a fact about you, not APL.
> I've talked to plenty of people whose eyes light up when you tell them 5 + 1 2 3 just works in the language.
This is literally "people who like it say they like it". It's like saying that the value proposition of Python is having list comprehension, when that's just syntactic sugar.
And no, it's not just me, you just have to take a look at the comments or even the OP. The only point being made is that it has very powerful array programming primitives, but those functions can be easily brought to other languages (and indeed a lot of them have those primitives).
Why is it? You're assuming that I know what "mod" means, what the parens and comma signify (and perhaps what the lack of space in mod( but the presense of space after the comma indicate). If I didn't know those thing, they aren't at all readable and there's no way to guess. Wouldn't it be "1 modulo 3" for readability? Look at beginners in various languages posting on the internet asking what the different parens, braces and brackets do and when to use them. And then you're assuming that I don't know %, which because it's so common in C-like languages, I do, but if I didn't the glyph looks like division.
It's easier to read "(AA|AB)([0-9A-F]{7,10})ZZ" than to write that regex in a flowchart of if/else branches and string and state manipulation. And easier to verify it's correct. Just because you can write unreadably hideously long regexes doesn't mean regexes should be shunned for unreadability, for a range of patterns they are usable, convenient and powerful.
> "I've never seen the value proposition of these family of languages other than "people who like them say they like them", and by the looks of the comments I don't seem to be the only one."
I'm not sure they have one in the large scale; I would like one as a rectangular-pattern-regex inside another language or shell because of the notation, and not in the sense that "you can do the same thing with numpy". I can write text search in Java, I'd rather use grep.
APL could serve the niche as a write only language. Not for code to be maintained, but for one offs. Of course, those one offs inevitably become code to be maintained, so maybe this niche doesn’t really exist.
It would also be a phenomenal spreadsheet language. the terseness means we get more visible code per cell block, the default data types mesh well, and the numerical orientation of the language is ideal.
I spent a fair bit of time with APL back in the 80s on IBM 3090 mainframe and a 3278 APL terminal and I have to say that if you've got an "APL shaped problem" and some familiarity with the language, it's easy to read and not so hard to maintain.
Learning curve is a bit steep, though...
don't you. think you can become better at it after some practice? let's compare that with mathematics notation or whatever... APL has less symbols than java's String methods.
I have never been able to shake the impression that APL is a troll language, like INTERCAL or brainfuck, except that part of the 'joke' is to insist that there's no joke. APL makes me feel gaslit.
The thing is, APL was originally designed as programming notation. It was used by Kenneth E. Iverson to discuss programming problems on blackboards with his students for years before it had an actual implementation.
So it has much more in common with math notation as a result, which is why the focus is on expressing yourself tersely with symbols. Also: nobody has to worry about special keyboards with symbols when writing those symbols with chalk on a blackboard, right? So the medium shaped the mode of communication.
I wonder what it would be like to learn programming that way, without using a physical computer. Maybe it would actually be a lot more pleasant for working through programming problems together? Mathematicians seem to be doing just fine working out their problems together on the blackboard with their notation after all.
EDIT: Hah, just saw that elsewhere in the thread an actual mathematician laments the (ab)use of symbols in their field, so I guess I was wrong there. Well, in that case, maybe mathematicians and programmers (or at least language designers) should join forces and see if they can come up with a more ergonomic notation together. A new Bourbaki club, so to speak.
> Roughly 86% of all the fun you get from APL programming comes from the mysterious symbols and the magic behind them. It’s not that APL is alien to computers, it’s just the computers were alien to APL for quite a while.
That's being alien to humans my dude. If understanding syntax is heaviest mental exercise in programming language it is unexcusably terrible. It should be considered torture to even require someone to read it.
that's rotating a matrix by one cell left, same, right: to make three matrices, then shifting those three up and down by one place to make nine with the 'surrounding squares' of each cell. Like so:
Quick, are their tuples correct or did I sneak in a mistake? Easier to eyeball ¯1 0 1 for correctness, right?
The whole APL expression is shorter than their line 14 to copy the board:
copy_board = [[board[row][col] for col in range(cols)] for row in range(rows)]
with its five variable names (easy to mistake col/cols and row/rows), four keywords, chained indexing inside a nested list comprehension. That's Followed by 12 more lines of code dense with 0s, 1s, <, >, >=, ==, row, rows, col, cols, neighbour, neighbours - quick are they all typo-free and all the comparison and boundary cases correct with no off-by-ones?
That leaves ∨.∧ as something weird, but it's short so you can try https://aplcart.info see if it's a known idiom or part of one, and if not it won't be much effort to type into a REPL like https://tryapl.org and play with. You can make some boolean arrays to try it in a few characters. What if you don't understand the Python code, how much more work is it to extract it into a test file and make test data and some print functions for it?
> "If understanding syntax is heaviest mental exercise in programming language it is unexcusably terrible. It should be considered torture to even require someone to read it"
The syntax is not the hard part, "how does this data transform solve the game of life" is the hard part, and hiding the data transform in a torrent of fluff like "(r < rows and r >= 0) and (c < cols and c >= 0) and (copy_board[r][c] == 1)" is inexcusably terrible. Details are what computers are good with, and what people are bad with. High level transforms is what people should focus on and tools should enable.
APL is extremely high level but terse. Brainfuck is extremely low level with a very limited alphabet of input tokens. The two bear really no similarities.
People who decry Perl as a "write-only language" have very rarely given Perl more than a cursory look. It has a lot of syntax, yes. It uses some shorthand symbols, yes, but not to the extent that APL does. The complaints about Perl from C or Python fans are just the same complaints Lisp folks have about C or Python syntax. Most of them are quick to proclaim how superior Python is. Oh, but Perl doesn't have invisible syntax nor a global interpreter lock.
Perl had the same problem as C++ and before that Ada have. There’s just too many ways to do things, so everyone use their own subset of the language and their own idioms. Which means that it’s hard for someone else to understand what you have done.
I wrote Perl for a couple of years early on in my software career. It's mostly write-only. Oh it can be used to write readable and maintainable code, but often isn't.
Poor analogies: none of what you describe is often the case out of deliberate action. Perl is often written deliberately in a way that is unreadable or maintainable. On purpose. Often because that’s what the language encourages.
In the rare occasions when I wrote Perl, the language offered all I needed to make my script as boring as Java, along with many temptations to be clever and concise; many people use Perl because it supports write-only "quackery", but it's their choice.
Let's be fair. APL is write-only whereas e.g. Python is not, only in the same sense math notation in scientific papers is write-only whereas elementary school summation is not - the former is much more ergonomic once you have the prerequisite knowledge and experience, but most people only need the latter to conduct their business.
If a programming language can be written it can be read, but readability refers to enjoyment. Everyone has a hobby, but I don't think the masses would ever find reading Brainfuck enjoyable even if they were experts who coded in it every day. Whereas there are languages that are geared towards readability, with talented developers working in those languages, when combined are able to produce something that is something you would be glad to sit down and read; up there with the best novels of our time.
Therefore my comparison with mathematical notation. It can be used to write beautiful things, but the reader needs to have some knowledge to understand it and appreciate its beauty. Most importantly though, the density/opaqueness of the notation is what allows it to lift powerful ideas - to express in few lines what would otherwise take many pages.
It's not brevity for brevity's sake - it's understanding the limits of human working memory. The more verbose a description gets, the harder it is to work with it, until at some point, you just can't process it at all (at this point people start making indexes or developing notation to... make things more concise).
Comparing APL with Brainfuck is just ridiculous. The former is designed to be dense to enable efficient work; the latter is a joke that's designed to be sparse.
> Comparing APL with Brainfuck is just ridiculous.
What ever do you mean? The comparison in the first comment (which I did not write) is apt. They both use a notation of symbols that are derived from Western notation. There was no further comparison with Brainfuck made.
In that comment, APL was further compared with Perl, which perhaps is what you meant to write? Perl's 'write-only' nature largely stems from its ingrained use of regular expressions. There are, indeed, some parallels between regular expressions and APL, both utilizing some kind of notation to concisely describe function.
On that note, regular expressions were the first programming language I ever learned and I feel I have a good handle on them. I still find no joy in reading them. It's simply not a good language to read, even if it can benefit on the write side. I'm not convinced that knowledge and experience makes something more enjoyable. There are a lot of things in life that I have plenty of knowledge and experience in that doesn't translate to enjoyment.
But regular expressions, one of the culprits of Perl unreadability, have not faded, and are very much alive in all languages, and usually in ways that make them even less readable than in Perl (e.g. requiring backslashes to be escaped, or without support for verbose formatting).
Sometimes we all just put up with the unreadable mess when it solves the problem at hand quickly.