Fixing APL’s trigonometric notation

2022-03-07

APL’s notation is beautiful. But there is one time where it falls short: when you would like to call a function, but must instead call another function which is really 12 functions hiding inside of a trenchcoat. is definitely the worst offender in this respect in classic APL; is the only other one I can think of, and any usage of is bound by nature to be ugly. (The axis operator arguably also qualifies, especially given the existence of atrocities like [0.5].)

(J, unfortunately, takes this idea and runs with it. Why write +.&.#:, which is completely sensible and coherent, when you could write 23 b.? It boggles. Also ;. (taken from Sharp, admittedly), !:, :, etc... At least F. and co are a step in the right direction. But I digress; I am here to complain about APL, not J.)

I also know, of course, that a great deal of thought went into the choice of assignment of numbers to to trigonometric functions, but it matters not. The scheme is still, as Paul Penfield puts it, ‘arbitrary’ and ‘distasteful’. I might, if I stretch, be able to recall that, because 1 is odd and 2 even, 1 denotes sine and 2 cosine, and thereby infer that 3 denotes tangent, but it is clear that the notation has none of the magic evoked by the famous anecdote about and . That said, I see such notation less as an inherent problem than as an admission that we have not yet come up with sufficient notation but must get on with life.

Emphasis on yet. Yet, in the decades following the introduction of , no one has dared try to replace it. The best anyone was able to come up with was a scheme for referring to the trigonometric functions by name, like plebian pointer-chasers; an improvement, perhaps, but a marginal one at best, and it seems a lot like an admission of defeat.

Well, it is a hard problem, and I do not blame anyone for it. Nevertheless, four and half paragraphs of patter hardly seem sufficient introduction for the idea I had, disturbing though it is in its utter simplicity. Here it is:

The sine function should be denoted by a ~.

Simple, easy, and obvious. It’s even ASCII! And ~’s old purpose can be served as well by ¬.0

Of course, this scheme has its problems too. How do you denote the other trigonometric functions? Cosine would, ideally, get a horizontally rotated version of ~. But ASCII has nothing like that, and Unicode has only slim pickings. Still, suppose we give up on interoperability entirely and draw the new glyph ourselves.1 Where does that leave us? Inverses can be derived using ⍣¯1. Tangent can be defined as a simple fork. The hyperbolic functions can be written by analogy to ~⍢𝑖 (where is the proposed ‘under’ and 𝑖 is a function which, when applied monadically, is 0j1∘×).

The other trigonometric functions do not look nearly so distinctive when graphed. Even if we were able to come up with funny squiggles which approximated them appropriately, it would not be worthwhile, as funny squiggles are valuable glyphographic real estate! Moreover, funny squiggles do not cohere with each other nearly to the extent that we would like; how could we make clear the connection between the sine-ish squiggle and the hyperbolic arctangent-ish squiggle, and also distinguish both from the subtraction squiggle?

Back to the drawing board, then? I think ~⍢𝑖⍣¯1 is a marked improvement over ¯5∘○, despite its verbosity (and APL could do with a dedicated ‘inverse’ operator, which would bring the two close to parity). Nevertheless, it is possible to do even better, and correct an inconsistency at the same time:

Glyph Function
Sine
Cosine
Tangent

No! Not my precious reverse and transpose glyphs!

Bear with me for a moment.

The glyphs I just stole were originally envisioned as matrix operations. When applied to a matrix, they flip its elements over the pictured axis of symmetry. Which is a very nice mnemonic, but leaves open the question of what happens to differently-ranked arrays. The choice was made that would reverse axes, would reverse major cells, and would reverse the elements of each minor cell. These choices were somewhat arbitrary. For instance, could have sorted its operand’s shape, enumerated its permutations, and chosen the first one following the current one. Or it could have rotated the shape by 1 (in either direction). could have applied along the second axis, rather than the last, and so on.

The point is that the suggestive power of the glyphs, though perhaps not entirely lost, was greatly diminished in the move to arbitrary-rank arrays and rank polymorphism. The final nail in the coffin of this visual paradigm was the introduction of rank: was obviated, effectively, because you could simply write ⊖⍤1 in its place. (Since major cells are sometimes displayed horizontally, and sometimes vertically, and are both unsuitable as general-purpose ‘reverse’ glyphs, barring major user interface changes which are probably not appropriate because of their effects on information density.)

It is instructive, then, to examine J and k’s treatment of these primitives. J’s |. is not dissimilar visually to , but there is one crucial difference between the two: the former does not emphasize the two-dimensionality of its operand. Moreover, |: (transpose) suggests not ‘mirror along some axis of symmetry’, but rather ‘reverse, but higher-order’. k’s | is analogous, and its + can be forgiven for being two-dimensional, as all it does is exchange the first two axes of its operand!

When used to denote the trigonometric functions, , , and gain suggestive power by relation to the circle function (whose utility, incidentally, is now brought into question, though I am sure somebody will come up with something useful; maybe it should find the angle between two vectors, or maybe it should be the complex number constructor2). They relate to complex numbers and trigonometry, which are inherently two-dimensional,3 and hence nothing is lost and much is gained by emphasizing the two-dimensionality of the operand.

Inverses are best expressed with a monadic operator, preferrably a visually distinctive one ( is somewhat noisy, even ignoring the ¯1 it must be suffixed with).

The hyperbolic functions are best expressed as inflected versions of the ordinary ones (cf ⌽', ⌽., or an overstruck if you are designing your own glyphs). It is tempting to give them the same foreground and a hyperbola-shaped background, but this obscures the relationship between them and their non-hyperbolic counterparts, and such glyphs would be less distinctive.

Of the remaining circle functions, the most interesting are those for extracting the real part, imaginary part, and angle of complex numbers (the remainder are sufficiently obscure that it is acceptable to define them manually at need). I do not have definitive answers for these. J’s +. and *. are very appropriate, but using monadic and for the same purpose seems much less appropriate. Paul Penfield discusses some options.

Postscript

Whom is this good for? I do not think many other people are doing greenfield APL designs nowadays. (And that is a bit of a shame.) Nevertheless, even when we stay rooted in place (whether for pragmatic reasons or others), it is important not to become complacent, and to continue to think of improvements to one’s current state.

I also have another, slightly ulterior motive, though. I think that, in APL circles and in general, semantics are sometimes elevated over syntax. I am guilty of this myself; I prefer J over (Dyalog,NARS,GNU...) APL because it has more coherent semantics (even though the syntax is inferior); I certainly do not claim it is an incorrect view. But I think it is important not to lose sight of the value (again) of good syntax.

Postpostscript

I also have an argument that should be the inverse cosine (yes, cosine), should be the inverse sine, and should be the inverse tangent. shows a line with a fixed x-component, and shows the two intersections of that line with a circle; which correspond to the two distinct angles that the inverse cosine function could sensibly return if given that x-component (being that cosine is only partially invertible). (An analogous argument holds for and .) Unfortunately, I think this argument only adds more confusion than it resolves (and it is not meant particularly seriously). It might be helpful to consider the gradient of the circle at the point where it intersects the line, but that is reaching at best.

Postpostpostscript

How the hell do we refer to reverse/rotate and transpose now?

Update

2022-03-14

How about for reverse/rotate, and the same symbol overstruck with a for transpose? Or simply ~ for reverse?

Notes

  1. (back)
    In fact, I came up with this scheme while pondering what, if anything, ~ should mean in an APL which used ¬ for logical negation.

  2. (back)
    Originally, this is what I planned to do, using Unicode’s private use area for a pseudo-textual representation. I care much less for interoperability than other people do.

  3. (back)
    In cartesian form, presumably, as it would be straightforward to write r×*○θ, which I think looks nice enough. Alternately, maybe should be polar and cartesian. Or maybe we don't care about poles and is better relegated to the hypergeometric. And there is still the question of whether 1p1 is an acceptable way of denoting pi. And ...

    Roger Hui discusses the issue here, and presents potential solutions.

  4. (back)
    Though NARS2000 famously supports for quaternions and octonions ... still. I think the argument still stands, though; if nothing else, a circle is a better model of a (hyper)sphere than of an array, but also: quaternions and octonions are much more obscure than high-rank arrays.

    Also, please ignore the Riemann sphere.