[nolan@nprescott.com] $  cat weblog archive feed

J as a CLI Calculator

2020-06-05

After reading julia as a cli calculator I thought it might be interesting to do a comparison with J.

In all fairness, I've never written any Julia and am relying entirely on the original article for explanation. This obvious deficiency is mediated by the fact that I am no expert in J either. Fun!

Plain Numbers

   7 % 4
1.75
   0j2 + 7 % 4      NB. add an imaginary part
1.75j2
   7r4 + 0j2        NB. I think the rational number looks nicer
1.75j2

   ^ 7r4 + 0j2      NB. exponential of complex number
_2.39476j5.23265

   +. ^ 7r4 + 0j2
_2.39476 5.23265    NB. splitting the real and imaginary parts

A Taste of J

Unlike Julia, J doesn't have anything like type annotations. The article doesn't write them directly but return type annotations do provide useful hints in some of the examples. In this regard J is more spartan, though I don't really miss it. The following is a 2x3 array of integers:

   1 2 3 ,: 4 5 6
1 2 3
4 5 6

While you could write this in a few different ways the only one I might consider would be as a reshape of a single array 2 3 $ 1 2 3 4 5 6, which is a little awkward here but I've used in the past when transcribing matrices out of another text verbatim. It seems Julia is pretty tolerant in the number of ways you might enter data, which is a little strange looking coming from J.

Julia achieves an inner product with just * which is reminiscent of operator overloading in C++ (and Python to a lesser extent). It looks pretty clean but I'm curious whether it gets confusing with mixed types. The J equivalent:

   ]A=. 1 2 3 ,: 4 5 6
1 2 3
4 5 6
   A +/ .* 3 2 1
10 28

There are a number of places where the arrangement of the returned values differs between Julia and J. In the above example to get a literal translation into a 2x1 vector rather than a 2 element array you could do something like: 2 1 $ A +/ .* 3 2 1, but I won't bother because as near as I can tell this is basically a style question for these examples.

Imperative Code

There are a number of examples of imperative code that I'll gloss over. The article really demonstrates that Julia is a multi-paradigm language and it looks like the designers didn't shy away from including a bit of everything (not unlike J's OOP in my opinion). Looping is uncommon in J though, where array operations are used instead. For example, printing the elements of a multidimensional array:

   echo"0 A
1
2
3
4
5
6

Rather than loop explicitly, the verb echo can be modified with the rank conjunction to act per element of the array.

Unicode Syntax

I was surprised to learn how much Julia uses unicode syntax. Coming from J and it's predecessor APL, it is weird to see unicode used in the case of a looping construct like [a^2 for a ∈ A]. The equivalent to this list comprehension in J is "square" applied to an array:

   *: A
 1  4  9
16 25 36

Rotation: an extended example

Finally we come to the part that got me interested in trying to write equivalent J code. The article poses the problem of rotating a triangle around the origin by an angle π/3. Deviating from a literal translation of the article, I define the vector of points a little more directly:

   ]V=. 0.5 0.5 ,. 1.5 0.5 ,. 0.5 1.5
0.5 1.5 0.5
0.5 0.5 1.5

The rotation matrix as defined in Julia is pretty neat looking, what I came up with is the following tacit definition of three forks:

   R=. (cos , -&sin) ,: (sin , cos)

The trigonometric definitions for sin and cos are 1&o. and 2&o. respectively. The value of pi is available through the monadic verb o. ("pi times"), where the argument becomes the multiplier to pi, here is pi and pi-over-three (which gets defined as "theta"):

   o. 1
3.14159
   ]theta=. o. 1r3
1.0472

The rotation matrix for π/3 is given by:

   R theta
     0.5 _0.866025
0.866025       0.5

At this point the article goes to some lengths to demonstrate the variety of paradigms Julia supports. I don't feel qualified to write about some of the more direct analogs in J, like fold, partly because I haven't yet needed them. J is most similar to the "linear algebra" approach described last in the article which takes the matrix product of the rotation and the vector of points:

   (R theta) +/ .* V
_0.183013 0.316987 _1.04904
 0.683013  1.54904  1.18301

In Julia the matrix determinant is loaded from the LinearAlgebra library, in J it is defined as -/ .*:

   det=. -/ .*
   det R theta
1

It is interesting to see modern languages experimenting with syntax like Julia's pipe operator but the result seems to miss the mark for me, much like unicode for-loops. I think there are more radical changes that can improve things like J's precedence rules (right to left).

Rotating around a corner

This piqued my interest because it is a chance to demonstrate a cool feature of J, the "under" conjunction. Rotating around a point involves first translating the triangle by the value of the point that remains fixed. If, for example, the rotation is around a point (3, 2) each point is translated by subtracting (3, 2); the rotation matrix is applied and then each point is added back the value (3, 2). Iverson saw this kind of do-undo pattern enough that it is designed into J. Here I'll define the translation as a subtraction bonded to the offset:

   offset=. 0 {"1 V          NB. 1st column of V
   mmul=. +/ .*
   translation=. -&offset    NB. partially apply one of two arguments
   
   (R theta)&mmul &.:translation V
0.5       1 _0.366025
0.5 1.36603         1

The language is smart enough that it can infer the "obverse" of subtracting an array (-&offset) is the addition of the same array — how cool is that?

Rotation around center

It is a little difficult for me to discern exactly how troublesome this is in Julia, the original article demonstrates the "linear algebra" way and then goes to some lengths to explain the "functional" way. There seems to be a good deal of complexity involved to reduce and achieve the "folding pattern" but I am not really clear on what it is solving. The first example of averaging the points of the triangle to find the center can be written like this:

   ]center=. V +/ .* 1r3
0.833333 0.833333

With the center defined, the rotation around center is pretty much exactly as it was for a corner, the translation is just redefined:

   translation=. -&center
   
   (R theta)&mmul &.:translation V
0.955342 1.45534 0.0893164
0.377992 1.24402  0.877992

Part of the complicated reduction example in the article involves taking the geometric mean of each row in a vector. It turns out that it is another instance where "under" shines, since geometric mean in J is really:

  1. log
  2. arithmetic mean
  3. anti-log
J "knows" that the obverse of log (^.) is anti-log, so the geometric mean of each row is as simple as (mean-under-log):
   mean=. +/ % #
   mean"1&.:^. V
0.721125 0.721125

Plotting

Here is a case where Julia is the clear winner; plotting in J is just not very well documented. I spent a few minutes looking into the libraries plot and graph, but the sparse documentation didn't give me much hope.

If the article hadn't relied on TikZ or if I didn't already know other plotting tools reasonably well this would have been more troubling to me. In the interest of completeness and because I still like gnuplot I took a minute to munge the data into a workable format. This required "wrapping" the points to include a repetition of the first point, so that gnuplot closes the polygon (triangle). Additionally, I've included a constant third column to colorize the triangles separately:

    (,&2"1) 4 $ |: (R theta)&mmul &.:translation V
-0.183013 0.683013 2
 0.316987  1.54904 2
 -1.04904  1.18301 2
-0.183013 0.683013 2

The distinct drawable regions can be separated by newlines so that the final file looks like this:

0.5 0.5 1
1.5 0.5 1
0.5 1.5 1
0.5 0.5 1

-0.183013 0.683013 2
 0.316987  1.54904 2
 -1.04904  1.18301 2
-0.183013 0.683013 2

 0.955342 0.377992 3
  1.45534  1.24402 3
0.0893164 0.877992 3
 0.955342 0.377992 3

      0.5     0.5 4
        1 1.36603 4
-0.366025       1 4
      0.5     0.5 4

Which can be plotted with gnuplot like this:

plot 'triangles.dat' with filledcurves linecolor variable

gnuplot result to prove it works

four triangles, rotated around the origin, a corner, and the
triangle center

Thoughts

Julia seems like a big, modern language with lots of packages and general niceties and while interesting, I don't think it is a language I will be learning myself. I appreciate the lengths the original post went to in order to demonstrate the different paradigms available but I can't help but feel like Julia is a really big language. I was pleasantly surprised with how readily I was able to translate the examples into the equivalent J, which is still a fun language for me. The relative ease with which I was able to throw things into gnuplot is certainly a point in J's favor here and is a nice reminder to keep the saw sharp.