Favorite Language Features

A tour of some of my favorite programming language features.

Function Trains

Sequences of functions in J form "trains" which determine how arguments flow from one function to another. For 3 functions, it's called a "fork", and it works like this:

NB. x and y are values, F G H are functions
x F G H y  NB. parsed as (x F y) G (x H y)
  F G H y  NB. parsed as (F y) G (H y)

Sequences of 2 functions are called "hook", and they work like this:

x F G y  NB. parsed as x F (G y)
  F G y  NB. parsed as y F (G y)

Why have this in a language? Why not just parse as a linear pipeline of functions? For a long time I didn't have an answer to these questions, but now I think I do. A recurring theme of the J language is to find the abstract patterns commonly used in computation and encode them in the language, and trains are one way to do that.

The most obvious example of "abstracting the pattern" is the fact that most functions in J operate on entire arrays. The abstract pattern is "loop over each element of an array" and so the language handles this, rather than placing that burden on the programmer.

Other examples of J trying to allow the programmer to omit unnecessary detail abound. For example, "power" operator (F ^:N y) applies the function F to its argument N times, feeding the result of the previous iteration in as the input to the next iteration.

F =: -:  NB. halve
N =: 4
y =: 100 32 1024
F ^:N y  NB. halve y 4 times

Without this feature, you could write the equivalent code by explicitly repeating the application yourself:

F =: -:
y =: 100 32 1024
F (F (F (F y)))

Or with a loop:

F =: -:
y =: 100 32 1024
{{ yy =. y
for. i.4 do. yy =. F yy end. yy }} y

But the handwritten form is tedious, and the loop obscures the meaning (repeat this action N times) with its assignment and control flow.

REPL

The read-eval-print-loop, or REPL, is the thing I appreciate most in a language. Interpreted languages usually rely on a REPL for interactive usage, but some ahead-of-time compiled languages have REPLs too.

The best REPL I've used to date is the JavaScript REPL embedded in Chromium-based browsers (such as Brave). Second best is the ipython shell. The feature they both share is intelligent history, so typing up-arrow doesn't give you the last line of a previously typed function, but the entire function. Practically speaking, this means if you're experimenting with a multi-line function and want to make an edit, you can type up-arrow once, make your edit, then type enter to re-evaluate the whole function. In this situation, simpler REPLs would require you to retype every expression of the function (or at least re-enter each line).

Function Definition

In K, write an anonymous function with curly braces:

{ x + y }

K also uses x for the first argument and y for the second argument (or z for the third argument, not used in this example).

Dictionary Construction

Constructing a dictionary (an associative mapping from keys to values) in JavaScript is convenient, because the language automatically treats bare words inside the curly braces as symbols:

//   key val  key val
d = {foo:123, bar:456};

These keys are accessible later using either dict.key or dict['key'] notation:

d.foo     // 123
d['foo']  // 123

By contrast, JSON requires quotes around the keys:

{"foo":123, "bar":456}

K lets you define dictionaires in a similar way (specify one (key, value) pair at a time), but it also allows you to specify all the keys and then all the values. This alternative syntax is interesting to me because it encourages you to think of dicts as a list of keys and a list of values, rather than a list of (key,value) pairs.

/   key key  val val
d: `foo`bar!(123;456)

Equality

Many languages confusingly use = to mean assignment. Thankfully, J takes a more sensible approach:

   x = x + 1  NB. no it doesn't
0

Other languages that also use = for equality include APL, K, SQL.

Types

In C, C++, Java, and others, type annotations are written with the type before the name. Many modern languages reverse the order, by writing name then type. First, to compare and contrast, here is C:

void foo(char a, double b){return;} /* function returning nothing, taking parameters of type char, then double */

However, Go, Rust, and Scala all reverse the type/name ordering:

fn foo(a:i8, b:f64) -> () {;}

Haskell goes a step farther, separating the types from the names by putting them on different lines:

foo :: Int -> Float -> ()
foo a b = ()

Some systems go farther still, and put type information in a separate file. The example of this that I'm most familiar with is json schema. When the cost of reading the code is high (e.g. network or rpc applications), this can be helpful.

Of course, type inference is the best kind of type annotation.

Function Application

In Haskell, juxtaposition is function application:

foo x = show x
foo "asdf"

This is syntactically "quieter" than languages like C, Java and Python, which require parentheses:

foo("asdf");

Transpose

APL's symbol for transpose () really evokes the idea, in my opinion:

      ⍝ transpose two rows, 3 columns into 3 rows, 2 columns
      ⍉ 2 3 ⍴ 1 2 3 10 20 30
1 10
2 20
3 30

Destructuring Assignment

Finally, some love for Python:

a,b,c = [10, 20, 30]

Links

If you're curious about the languages and other technologies mentioned in this post and want to learn more, check out some of these links: