J as a Desktop Calculator
Talk Tuesday
I'm speaking at Berlin Function Programming Group! Noon CST tomorrow, register here. It's my standard TLA+ talk, so there won't be any major surprises if you've heard a version of it before. Still, if you haven't, here's your chance :)
New Schedule
Last year I tried updating twice a week: once publicly on Mondays, once privately on Fridays. In the end, I wasn't able to keep that up: I published private newsletters only half the time or so. It's only gotten worse this year: I've only sent private newsletters twice! Going forward, I'm going to stick to one public newsletter a week by default. I might still publish sneak peeks privately on Fridays, but that will be the exception, not the norm.
(Upside is I want to write more polished pieces for The Blog, so more should be coming out for that.)
J as a Desktop Calculator
Yesterday I was working on a post about TLA+ optimizations and ran into a fun problem: imagine we have m workers w₁, w₂, ... wₘ. The first one takes N₁ steps to complete, the second N₂, etc. Each worker can run between any two steps of another worker. How many possible interleavings are there total?
Let's start with two workers, first takes two steps (A, B), second takes one (C). There are six possible orderings of ABC:
ABC BAC
ACB BCA
CAB CBA
However, the first worker is sequential: it must run A before B. Of the 2! ways we can permute AB, only one of them is valid, so only 1/(2!) of the total interleavings are valid. If both workers were two steps long, then there'd be (2+2)! total interleavings, and we'd have to divide by 2! for each worker. Generalizing this, we get:1
(N₁+N₂+...+Nₘ)!
--------------
N₁!*N₂!*...*Nₘ!
In Python, that'd be:
from math import factorial, prod
def num_interleaves(l):
a = factorial(sum(l))
b = prod(map(factorial, l))
return a / b
75ish characters, not counting names or imports. In J, it's
ni =: !@:(+/) % */@:!
15 characters. Normally this would be a pointless comparison: saving 60 doesn't mean anything for the quality of the program or our ability to write it. If I'm interactively crunching numbers, though, terseness is a big deal. I might want to tweak my script a bunch of write one-off queries. In those cases, the amount of work required to change the Python adds up a lot more quickly than the work to change J. As a quick test, I changed the Python to this:
a = sum(l)**2
b = prod(map(lambda x: x**2, l))
return a / b
That took me about 40 keystrokes, including navigation. Here's the corresponding changed J:
*:@:(+/) % */@:*:
Just about 9 keystrokes. When you're constantly changing things, those kinds of differences add up. This of course doesn't scale up infinitely: you only get this kind of terseness if you can easily compose your verb out of the base primitives. Otherwise, things get much, much nastier. I wouldn't want to use J to do curve-fitting or analyze a dataframe.
But as a powerful desktop calculator? Sure. A lot of J's "weirdness" works in favor here. Terse operators? You can type them faster. 250+ verbs? You don't have to import anything. Strict right-to-left evaluation? You can always tack additional operations to the end beginning and not worry about precedence. Tacit verbs? You can write and execute them as one line!
Also, it fits in with my impression that J was intended purely for interactive use. My evidence here is that the UX for non-interactive use is atrocious. As far as I can tell, there's no way to get J to terminate automatically: you have to include an exit ''
at the end of your script. This means that if there's an error, you'll be dumped into the interactive session. There's also other fun issues like "batch J doesn't automatically seed random", which is great fun when your script works interactively but is mysteriously deterministic in batch. On the other hand, the interactive tooling is quite nice! If you're running jqt
(you should) you can pop open an editor with ctrl+M
(you should).
I don't know if J is worth learning purely because for desktop-calculator purposes, but it's a good way to use it if you already know it. I keep running into small calculations I have to do. Things like "If our test is correct 95% of the time and we make 10 tests, what's the chance we'll have N false results?"
100*(%+/)#/.~/:~(+/"1)0.95&<?5e6 10$0
59.8776 31.5148 7.45942 1.0455 0.09612 0.00626 0.0003 2e_5
Or "what's the largest row of Pascal's Triangle that fits in a tweet, assuming one space between each number?"
<: {. I. 280 < #@":@(!~ i.@>:)"0 i. 40
34
(Look, I have weird problems.)
Some tips for using J as a desktop calculator:
- Use jqt. It doesn't come in all distro packages, so you might need to download it from the J package manager.
- Read one of the free books, like Learning J.
- Start with a minimal set of verbs, like the ones here. On top of the primitives there, I'd recommend learning
@:
,/
, and~
. After that you can go onto the NuVoc. - Most of the verbs pull septuple-duty with use cases. Like
x # y
can be used to clone rows of y, but also can be used to select/filter rows in y, ifx
is a binary string. You only need to know the most common uses cases. Then again, knowing what the most common use cases are can be a tricky thing. - Strings are your sworn enemy. If you need to do any kind of string processing, you probably don't want J.
- dissect is your friend. You can get it through the package manager.
- There's a standard library! Most people don't know about it. I didn't for two years!
I'm not expecting anybody to learn J from this, just wanted to talk about a fun way I use a niche language. But hey, if anybody does decide to start, happy to answer questions =)
-
It's intuitive why this would always give a whole number, but I'm not immediately seeing how to prove that. Something to save for the weekend, maybe! ↩
If you're reading this on the web, you can subscribe here. Updates are once a week. My main website is here.
My new book, Logic for Programmers, is now in early access! Get it here.