Functional programming, APL and Unix pipes

by George Pollard

I’ve noticed that when reading rather deeply-nested functional code in Haskell, the process taking place in reading the code seems to resemble trying to decipher APL more than anything else. This seems to be because functions which operate over lists are easily concatenated (possibly using the $ operator), because they operate on lists in their last parameters. For example:

foldr getCounts (0,0,0) $ map classify $ concat [[20..30], [490..500], [8120..8130]]

In order to understand what is happening here, one needs to start from the RHS of the expression and then work backwards—much the same as in this snippet of APL:

(∼R∈R∘.×R)/R←1↓⍳R

(See the Wikipedia article on APL for what is happening here.)

In both cases, the code is operating upon a common parameter which is modified by each function in turn. Similarly, there seems to be a rather high-level isomorphism between these kinds of functional code and the concept of Unix pipes:

sort file | uniq -c | sort -n

…with the difference being that in this case, the common parameter is being passed from the left instead of from the right.

Now, in Haskell I can define a new “pipe” operator (| is already taken):

(~>) :: a -> (a->b) -> b
(~>) x f = f x

Which just has the reverse signature to the $ operator, and:

foldr getCounts (0,0,0) $ map classify $ concat [[20..30], [490..500], [8120..8130]]

…becomes…

[[20..30], [490..500], [8120..8130]] ~> concat ~> map classify ~> foldr getCounts (0,0,0)

…which makes it a bit easier to read, at least in my (used-to-Linux) eyes.