0
votes

I have the following Haskell expression:

let increment x = 1 + x
in \xs -> map chr (map increment (map ord xs))

I know that the above expression takes a list, and applies the ord function to each element in the list. For a list of chars, the result would be a list of integers (the ASCII value). The increment function then increments each integer in the list by 1. The chr function then converts each integer back into its corresponding character.

And I also have the following expression:

map chr . map (1+) . map ord

I am trying to figure out whether the above is equivalent to the first expression. However when I tried the above expression in GHCI, I got an error:

map chr . map (1+) . map ord ['a', 'b', 'c', 'd', 'e']

I am not sure why the expression didn't work. Since function composition is used, wouldn't the expression be evaluated as:

(map chr (map (1+) (map ord ['a', 'b', 'c', 'd', 'e'])))

With map (1+) getting a list as a result of map ord, and map chr getting a list as a result of map (1+)?

1
You need brackets: (map chr . map (1+) . map ord) ['a', 'b', 'c', 'd', 'e']. Otherwise it gets evaluated as map chr . map (1+) . (map ord ['a', 'b', 'c', 'd', 'e']), which doesn't work as that last bit is a value rather than a function. - bradrn
(Also, map chr . map (1+) . map ord is equivalent to map (chr . (1+) . ord).) - bradrn
Lol, in the time it took me to write my answer @bradrn has said just about everything in it in 2 short comments :) - Robin Zigmond
@RobinZigmond But your answer went into much more detail! While I didn't even bother explaining it properly... - bradrn

1 Answers

5
votes

Operators (defined as functions with no alphanumeric characters in their name, which are used in infix style) have lower precedence than other functions in Haskell. Therefore your expression:

map chr . map (1+) . map ord ['a', 'b', 'c', 'd', 'e']

is parsed as:

(map chr) . (map (1+)) . (map ord ['a', 'b', 'c', 'd', 'e'])

And this doesn't make any sense - the . operator combines 2 functions, and map ord ['a', 'b', 'c', 'd', 'e'] is not a function, but a list of numbers.

But there are still better alternatives to your correct version with all the nested parentheses. What you want to do is start with the list of characters, and successively map 3 functions over it. That, by definition of function composition, is the same as applying the composition of the 3 map s to it, ie:

(map chr . map (1+) . map ord) ['a', 'b', 'c', 'd', 'e']

which as @chi points out below, would often be written like this, using the function application operator $ (which has lower precedence than any other operator) to avoid the need for any parentheses:

map chr . map (1+) . map ord $ ['a', 'b', 'c', 'd', 'e']

And, because map f . map g is always the same as map (f . g) (this should be obvious when you stop to think about what these expressions mean - it's also the fundamental law the map operation must satisfy in order to make the list type into a Functor), this can be alternatively written as:

map (chr . (1+) . ord) ['a', 'b', 'c', 'd', 'e']

In my opinion this is the best and most readable version.

Although even better, as @chi again points out, is to use that chr . (1+) . ord is equivalent to succ, the built-in function for getting the "next" value of a type that can be enumerated. So you can just write map succ ['a', 'b', 'c', 'd', 'e']