Playing Around With J
I pasted the following into a group chat with some friends.
"J is pretty great"
|syntax error
| "J is pretty great"
"I don't know how to do strings in it"
|open quote
| "I don't know how to do strings in it"
| ^
"It has normal infix notation"
"It has normal infix notation"
|syntax error
| "It has normal infix notation"
5 + 3
8
NB. Adorably, you start comments with "NB."
NB. J is an offshoot of APL.
NB. APL has a fun history.
NB. Ken Iverson was a math prof @ Harvard, and he got sick of math notation
NB. (Just my kind of guy)
NB. So he made up his own, and brought in a bunch of his own symbols
NB. It was just that---notation, had nothing to do with computers---
NB. until he went to go work for IBM.
NB. They wanted him to name his new programming language, but Iverson, who
NB. just wanted to change the notation mathematicians used, refused.
NB. But they were paying his salary, so he opted for malicious compliance.
NB. He called it "APL," which stands for..."A Programming Language."
NB. I don't plan on learning APL any time soon, but Iverson later came up with
NB. J, which uses ASCII.
NB. I am still not good at J
NB. But it has some things I really like.
NB. The biggest is that it is dedicated to terseness, and is willing to put a lot of work in towards that goal
NB. In javascript you'd increment all members of an array with something like this:
NB. [1,2,3,4,5].map(i => i+1)
NB. That's the short version, in the bad old days you'd have to do something like: [1,2,3,4,5].map(function (i){return i+1;})
NB. or even worse:
NB. arr = [1,2,3,4,5]
NB. for (i=0;i<arr.length;i++){
NB. arr[i]+=1;
NB. }
NB. In J it's just:
1 2 3 4 5 + 1
2 3 4 5 6
NB. Oooo, wow, list comprehension! It's OK if you're not super impresssed. But the neat thing is that it's baked into the + operator
NB. It might not seem like a big deal to be able to do 1 + 1 2 3 4 5 instead of (map inc '(1 2 3 4 5)), but if you apply that philosophy to EVERYTHING it starts to add up
NB. For instance summing a list in clojure is done like this: (reduce + [1 2 3 4 5])
NB. In J it's just: +/
+/ 1 2 3 4 5
15
NB. Four parentheses/brackets, five letters, and a space; Clojure uses 13 characters for stuff that isn't data, J uses 3.
NB. And this with a data literal! In application that array will be a symbol like a:
a=. 1 2 3 4 5 NB. but really: a=.$RANDOM WEBSERVICE OR DB CALL
+/a
15
NB. So three characters! In clojure I might have done something like this:
NB. (let [a '(1 2 3 4 5)
NB. total (reduce + a)]
NB. total)
NB. Note that the *name* "total" is longer than J's *implementation* of +/a!
NB. The thing that makes this possible is heavy, heavy polymorphism.
NB. The core functions of J are heavily overloaded, able to infer a lot from what arguments you give them.
NB. Like it's a no-brainer that + does scalar addition if you feed it two numbers...
NB. But what if you feed it two functions?
*: 5 NB. *: is the character to "square" something
25
% 5 NB. %, when given a single arg, gives the reciprocal of something
0.2
mysteryfn =: *: + % NB. So what if you add the square fn and the reciprocal fn together?
mysteryfn 5
25.2
NB. It returns another function that acts on the results of applying the two functions originally given; in javascript this would be:
NB. function + (arg1, arg2){
NB. if (function?(arg1) and function?(arg2)){
NB. return function(arg){
NB. return (arg1(arg) + arg2(arg));}}}
NB. The funny part is that I've been pulling punches here a bit.
NB. I wrote mysteryfn as *: + %, but a real J programmer would write it as *:+%
NB. Now, I'm no stranger to functions-that-return-functions
NB. That's a very Lispy thing, and I've played around a bit with Haskell, which goes much deeper into it.
NB. But J *assumes it*, baking it into its basic operators
NB. Getting that same functionality in clojure without overwriting + is kinda verbose: , (defn make-like-j [f]
NB. (fn [arg1 arg2] ;don't worry about understanding this code
NB. (if (every? fn? [f1 f2])
NB. (fn [arg] (+ (f1 arg) (f2 arg)))))
NB.
NB. And then when called, I'd have ((make-like-j +) reciprocal square)
NB. Even if we shortened all the names: ((j +) r s)
NB. Still not quite the same as (*:+%)
NB. So what's the big takeaway here? Why did I send you this?
NB. First: that terseness has a magic all its own. Having a full program in *literally a single line* has all sorts of great side effects: easy to look at, easy to hold in your head, easy to *share and talk about*.
NB. Short symbols impose a low cost on screen real estate, encouraging composition. It's not enough to be powerful; programs must also be efficient in their use of resources. And yes, character count is one of those resources.
NB.
NB. Also, easy to type. One phrase I'm fond of is "Ironman technology," as opposed to "tank technology." Ironman's suit is meant for an individual; a tank is meant to be used by a nation-state. What if we had heroes all around us, but no swords? You can write this on a phone in the desert, easy peasy.
NB. Second is how powerful built-in polymorphism of this type is. Seriously, look at how the humble + reacts to different arguments:
1 + 2
3
1 + 2 3
3 4
2 3 + 3 5
5 8
(*:+%) 5
25.2
NB. And that's just the *implicit* stuff! If you're willing to be explicit about going meta (which, like everything in J, means adding like two characters), a whole lot more is available.
NB. And that's just the *implicit* stuff! If you're willing to be explicit about going meta (which, like everything in J, means adding like two characters), a whole lot more is available.
NB. The phrase that comes to mind with J is "DNA made out of diamond": insanely compact, with a couple more magnitudes of meaning than anyone is used to, relative to size. Very much feels smartphone-optimized, even though it was written in the early 90's, and APL about thirty years before that.
NB.
NB. One last thought: The big sell of Lisps is that "Lisp isn't a language, it's a building material." In other words, Lisp is really great for implementing a mini-language perfectly optimized towards your problem. J feels like something I'd be proud to have built in Lisp; a finished product.
NB. Unfortunately I couldn't find an online repl for J anywhere, but it's super lightweight if you want to try it out: https://code.jsoftware.com/wiki/System/Installation/J901
"J is pretty great"
|syntax error
| "J is pretty great"
"I don't know how to do strings in it"
|open quote
| "I don't know how to do strings in it"
| ^
"It has normal infix notation"
"It has normal infix notation"
|syntax error
| "It has normal infix notation"
5 + 3
8
NB. Adorably, you start comments with "NB."
NB. J is an offshoot of APL.
NB. APL has a fun history.
NB. Ken Iverson was a math prof @ Harvard, and he got sick of math notation
NB. (Just my kind of guy)
NB. So he made up his own, and brought in a bunch of his own symbols
NB. It was just that---notation, had nothing to do with computers---
NB. until he went to go work for IBM.
NB. They wanted him to name his new programming language, but Iverson, who
NB. just wanted to change the notation mathematicians used, refused.
NB. But they were paying his salary, so he opted for malicious compliance.
NB. He called it "APL," which stands for..."A Programming Language."
NB. I don't plan on learning APL any time soon, but Iverson later came up with
NB. J, which uses ASCII.
NB. I am still not good at J
NB. But it has some things I really like.
NB. The biggest is that it is dedicated to terseness, and is willing to put a lot of work in towards that goal
NB. In javascript you'd increment all members of an array with something like this:
NB. [1,2,3,4,5].map(i => i+1)
NB. That's the short version, in the bad old days you'd have to do something like: [1,2,3,4,5].map(function (i){return i+1;})
NB. or even worse:
NB. arr = [1,2,3,4,5]
NB. for (i=0;i<arr.length;i++){
NB. arr[i]+=1;
NB. }
NB. In J it's just:
1 2 3 4 5 + 1
2 3 4 5 6
NB. Oooo, wow, list comprehension! It's OK if you're not super impresssed. But the neat thing is that it's baked into the + operator
NB. It might not seem like a big deal to be able to do 1 + 1 2 3 4 5 instead of (map inc '(1 2 3 4 5)), but if you apply that philosophy to EVERYTHING it starts to add up
NB. For instance summing a list in clojure is done like this: (reduce + [1 2 3 4 5])
NB. In J it's just: +/
+/ 1 2 3 4 5
15
NB. Four parentheses/brackets, five letters, and a space; Clojure uses 13 characters for stuff that isn't data, J uses 3.
NB. And this with a data literal! In application that array will be a symbol like a:
a=. 1 2 3 4 5 NB. but really: a=.$RANDOM WEBSERVICE OR DB CALL
+/a
15
NB. So three characters! In clojure I might have done something like this:
NB. (let [a '(1 2 3 4 5)
NB. total (reduce + a)]
NB. total)
NB. Note that the *name* "total" is longer than J's *implementation* of +/a!
NB. The thing that makes this possible is heavy, heavy polymorphism.
NB. The core functions of J are heavily overloaded, able to infer a lot from what arguments you give them.
NB. Like it's a no-brainer that + does scalar addition if you feed it two numbers...
NB. But what if you feed it two functions?
*: 5 NB. *: is the character to "square" something
25
% 5 NB. %, when given a single arg, gives the reciprocal of something
0.2
mysteryfn =: *: + % NB. So what if you add the square fn and the reciprocal fn together?
mysteryfn 5
25.2
NB. It returns another function that acts on the results of applying the two functions originally given; in javascript this would be:
NB. function + (arg1, arg2){
NB. if (function?(arg1) and function?(arg2)){
NB. return function(arg){
NB. return (arg1(arg) + arg2(arg));}}}
NB. The funny part is that I've been pulling punches here a bit.
NB. I wrote mysteryfn as *: + %, but a real J programmer would write it as *:+%
NB. Now, I'm no stranger to functions-that-return-functions
NB. That's a very Lispy thing, and I've played around a bit with Haskell, which goes much deeper into it.
NB. But J *assumes it*, baking it into its basic operators
NB. Getting that same functionality in clojure without overwriting + is kinda verbose: , (defn make-like-j [f]
NB. (fn [arg1 arg2] ;don't worry about understanding this code
NB. (if (every? fn? [f1 f2])
NB. (fn [arg] (+ (f1 arg) (f2 arg)))))
NB.
NB. And then when called, I'd have ((make-like-j +) reciprocal square)
NB. Even if we shortened all the names: ((j +) r s)
NB. Still not quite the same as (*:+%)
NB. So what's the big takeaway here? Why did I send you this?
NB. First: that terseness has a magic all its own. Having a full program in *literally a single line* has all sorts of great side effects: easy to look at, easy to hold in your head, easy to *share and talk about*.
NB. Short symbols impose a low cost on screen real estate, encouraging composition. It's not enough to be powerful; programs must also be efficient in their use of resources. And yes, character count is one of those resources.
NB.
NB. Also, easy to type. One phrase I'm fond of is "Ironman technology," as opposed to "tank technology." Ironman's suit is meant for an individual; a tank is meant to be used by a nation-state. What if we had heroes all around us, but no swords? You can write this on a phone in the desert, easy peasy.
NB. Second is how powerful built-in polymorphism of this type is. Seriously, look at how the humble + reacts to different arguments:
1 + 2
3
1 + 2 3
3 4
2 3 + 3 5
5 8
(*:+%) 5
25.2
NB. And that's just the *implicit* stuff! If you're willing to be explicit about going meta (which, like everything in J, means adding like two characters), a whole lot more is available.
NB. And that's just the *implicit* stuff! If you're willing to be explicit about going meta (which, like everything in J, means adding like two characters), a whole lot more is available.
NB. The phrase that comes to mind with J is "DNA made out of diamond": insanely compact, with a couple more magnitudes of meaning than anyone is used to, relative to size. Very much feels smartphone-optimized, even though it was written in the early 90's, and APL about thirty years before that.
NB.
NB. One last thought: The big sell of Lisps is that "Lisp isn't a language, it's a building material." In other words, Lisp is really great for implementing a mini-language perfectly optimized towards your problem. J feels like something I'd be proud to have built in Lisp; a finished product.
NB. Unfortunately I couldn't find an online repl for J anywhere, but it's super lightweight if you want to try it out: https://code.jsoftware.com/wiki/System/Installation/J901
You can find a light weight J REPL here:
ReplyDeletehttps://joebo.github.io/j-emscripten/
'J is great'
ReplyDeleteJ is great
NB. Single quotes are used to distinguish character vectors; include them in the vec by doubling:
'It isn''t hard.'
It isn't hard.
NB. A double quote is used for unrelated things - it's called the "rank" conjunction:
i. 2 3 NB. A 2x3 table
0 1 2
3 4 5
< i. 2 3 NB. Box the whole table
+-----+
|0 1 2|
|3 4 5|
+-----+
<"1 i. 2 3 NB. Box the 1D sub-arrays of the table.
+-----+-----+
|0 1 2|3 4 5|
+-----+-----+
<"0 i. 2 3 NB. Box the 0D sub-arrays (scalars)
+-+-+-+
|0|1|2|
+-+-+-+
|3|4|5|
+-+-+-+
This is great! Thank you!
Delete