2

The first problem in 99 Haskell Problems is to "find the last element of a list". I came up with two solutions:

Solution 1 (This works)

myLast :: [a] -> a
myLast = head . reverse

Solution 2 (This doesn't work)

myLast :: [a] -> a
myLast = head $ reverse

Question

  • Why does solution 1 work but not solution 2? What I'm most confused by is how neither implementations supply pattern matching.
Jon
  • 9,815
  • 9
  • 46
  • 67

2 Answers2

7

head is a function of one argument1. If you use f $ x to apply a function to something, it's the same as simply writing f x (or if you like f(x), or (f)x, all the same thing... just uglier): the argument is "filled in" by the specified variable. So the result of head $ reverse will simply be whatever result head gives you when fed with an argument of reverse's type... ...which however doesn't work, because head needs a list but reverse is a function. $ itself doesn't care about this but just hands on the parameter, for instance you could write

Prelude> :t map $ reverse
map $ reverse :: [[a]] -> [[a]]

because the first argument of map is in fact a function.

With (.) it's different. That cares about what type the argument to its right has (must also be a function), and it doesn't simply feed it to the left function right away. Rather, f . g yields another function, which does the following: it waits for some argument x, which it feeds to g, and then feeds the result of that to f.

Now, if you write

myLast' = head . reverse

it means just, you define myLast as this function that (.) gives you as the composition of head and reverse. That there are no arguments mentioned for myLast here doesn't matter: [a] -> a is just some type so you can define variables with this type (like myLast), by assigning them to values that happen to have such a function type (like head . reverse). You could, if you wanted, make the parameter explicit:

myLast'' x = (head . reverse) x

Note that the parens are needed because otherwise it's parsed as head . (reverse x) – which wouldn't work, because reverse x is not a function anymore, just a result list. And therefore you couldn't compose it with head; what you could however do is apply head to it:

myLast''' x = head $ reverse x

1In fact, every function in Haskell has just one argument... but we say "two-argument function" for things like (+) :: Int -> Int -> Int, though really this is a one-argument function returning a one-argument function returning an Int: (+) :: Int -> (Int -> Int).

leftaroundabout
  • 117,950
  • 5
  • 174
  • 319
2

Roughly, application ($) is used between a function and its argument (a list in this case), while composition (.) is used between two functions.

About pattern matching: in the first solution, the function reverse will perform the needed pattern match for you, so myLast does not have to.

chi
  • 111,837
  • 3
  • 133
  • 218