In a 1988 paper called Algebraic Identities for Program Calculation, Richard Bird wrote:

Probably the most useful law about map is the fact that it distributes over functional composition: (map f) . (map g) = map (f . g)

Bird’s paper predates Haskell, and instead uses a language called Miranda, but Haskell later adopted very similar notation:

GHCi, version 6.12.1: http://www.haskell.org/ghc/  :? for help
Prelude> let f x = x * x
Prelude> let g x = x + 1

Prelude> ((map f) . (map g)) [1 .. 5]
[4,9,16,25,36]

Prelude> map (f.g) [1 .. 5]
[4,9,16,25,36]

I am a sucker for nice notation, and that is some very nice notation. It got me thinking about how I might write the same thing in Clojure. Here is my first cut at a literal translation:

user> ((comp (partial map sq) (partial map inc)) (range 1 6))
(4 9 16 25 36)

user> (map (comp sq inc) (range 1 6))
(4 9 16 25 36)

The Clojure code looks a little noisy by comparison, mostly due to the explicit use of partial. I came up with a couple of variations on map that clean up the presentation. The first I call mapp:

(defn mapp
  ([f] (partial map f))
  ([f x & args]
     (apply map (partial f x)
            args )))

Here are some examples of mapp in action:

;; Using regular old map
user> ((comp (partial map sq) (partial map inc)) (range 1 6))
(4 9 16 25 36)

;; We can clean that up a bit with mapp
user> ((comp (mapp sq) (mapp inc)) (range 1 6))
(4 9 16 25 36)

;; Or you can do stuff like this 
user> (mapp * 100 (range 6))
(0 100 200 300 400 500)

user> (mapp + 1/2 (range 6))
(1/2 3/2 5/2 7/2 9/2 11/2)

My other variation on map is called mapc.

(defn mapc [& args]
  (let [[fns xs] (partition-by fn? args)
        g (apply comp fns)]
    (if (empty? xs)
      (partial map g)
      (apply map g xs ))))

Here are some examples of mapc in action.

;; Map the (implicit) composition of two functions
user> (mapc sq inc (range 1 6))
(4 9 16 25 36)

;; We can compose more than two functions
user> (mapc inc sq inc (range 1 6))
(5 10 17 26 37)

;; Or use mapc like plain old map
user> (mapc sq (range 1 6))
(1 4 9 16 25)

;; Or by providing only functional args, produce a function
user> ((mapc sq inc) (range 1 6))
(4 9 16 25 36)

To wrap this up, note that I wanted to come close to something like

(map f) . (map g) = map (f . g)

The best I could do was

(comp (mapp f) (mapp g))  =  (mapc f g) 

That’s not half bad. I rather like mapp and mapc. But I genuinely wonder whether anyone else will. Play around with mapp and mapc, let me know what you think.

Update: In the comments, MarkusQ pointed out that (comp (mapp f) (mapp g)) = (mapp (comp f g)). In particular,

user> ((comp (mapp sq) (mapp inc)) (range 1 6))
(4 9 16 25 36)

user> ((mapp (comp sq inc)) (range 1 6))
(4 9 16 25 36)

MarkusQ’s equation preserves symmetry by using mapp on both side of the equals sign. I think this is much prettier than the version I came up with. Keats observed that “Beauty is truth, truth beauty…”. The version Markus sent in makes me believe that mapp may well be a worthwhile abstraction.