APL Notes: BQN Lacks

BQN Lacks

Potential frustrations

BQN has 64-bit floats only, which means only up to 52 bits (+sign) of representation for integers, and no 64-bit bitwise operations without breaking the floating representation.

BQN has shallow function call stacks, probably a few thousand on JS and 4096 on CBQN, and no tail recursion, so depth-first pathfinding might need to be reworked to avoid these limits.

BQN is incompletely and inconsistently implemented: no implementation has complex numbers though they're specified, and bqn.js doesn't even have •HashMap and seems to have fallen behind a bit.

These limitations though are in the service of a couple of goals that you might appreciate:

  1. BQN should have good compatibility across implementations, except for clearly flagged system functions. It makes sense that code using a custom •Plot added to a bqn.js site would need some additional care to get working on CBQN, but array code should just work.
  2. The implementations themselves should have very good host integration. I easily added •HashMap to this site's bqn.js to get memoization examples to work, and CBQN has a very reasonable C FFI.

vs. J

While going through my old J notes to see what can be translated to BQN, I noticed that my complaints often don't translate at all:

But of course, J has a lot of stuff that I didn't complain about, so don't have notes for, that BQN doesn't have. There's obvious stuff like well-developed libraries in the package system, jqt, jhs, jd. J's much older and was commercial for a long time so it has lots of quality code on its side. There's also the ASCII characters which eliminate a lot of low-grade hassle with fonts or having to start bqn just to use its input method to copy&paste some characters.

Other points in J's favor:

   16bffff  NB. selectable numeric base for numbers
65535
   1r8*2    NB. rational numbers
1r4
   !40x     NB. bignums
815915283247897734345611269596115894272000000000
   +1j1     NB. complex numbers
1j_1
   NB. I forgot these even existed:
   1p1 1p2
3.14159 9.8696
   1x1 1x2
2.71828 7.38906
   $. 10 ?@$ 2   NB. sparse arrays
0 │ 1
1 │ 1
2 │ 1
6 │ 1
8 │ 1
9 │ 1
   0 : 0
HERE document syntax.
)
   3 XOR 16bffffffffffff  NB. xor on >32 bit numbers
281474976710652
   0 0$i.400
   NB. ability to suppress output entirely

   (3 : 'echo 1') 10   NB. a monadic verb with no args referenced
1

J's Def and dfuns mostly compare unfavorably to BQN blocks (on looks, on scope), but I've had several errors due to blocks getting interpreted as immediate instead of as functions, and the ways to avoid that are a little weird.

J primitives that BQN lacks

Trivial

<: >: (decrement and increment), +: *: -: (double, square, halve)
   ⟨-⟜1, +⟜1, ×⟜2, ט, ÷⟜2⟩{𝕎𝕩}¨ 5
⟨ 4 6 10 25 2.5 ⟩

Mathematical

! #. #: j. m. p. p.. p: q: r. x: . (matrix product) ^!.p +. *. %. c. H.

Especially with the deprecated calculus primitives, these suggest J's older focus and customer base. Immediate factorization and #.inv made J very handy.

There are some hidden lacks here:

   */inv 120
2 2 2 3 5

   ×´⁼120
Error: ⁼: Inverse not found
at ×´⁼120
   ^^^

Data structures

S: L: {:: /. $. (Sparse)

The first three plumb deeply nested boxes in particular ways. The last two can be replaced by •HashMap

Control flow

F. (fold family of primitives) try. catch. goto_lbl. ^:

J has many more options for advanced control flow, and especially stack-neutral control flow, but it's all very hard to recommend over simpler options.

Systems

t. T. (threads/tasks)

This is nice, but so involved I wonder why it's not under !:

Swiss Army Knife

;: (sequential machine) .; (cut) !. (customize)

These are poster children for arcane, non-primitive primitives.

Odd/Utility

= (Self-Classify) M. (Memo) A. (Anagram) C. (Permute)

M. is a less flexible BQN oneliner.