Search code examples
functionhaskellfunctional-programmingcomposition

Haskell function composition methods


I just started learning Haskell at uni, and while playing around with it, I stumbled upon a problem that I can't seem to grasp. The following code gives me the desired result:

import Data.List
list = ["Hello", "world"]

main = print $ intercalate " something " (reverse (map reverse list))

Output:

"dlrow something olleH"

But I want to write the 'main' function with dots instead of brackets, so it tried:

main = print $ intercalate " something " . reverse . map reverse list

However, this gives me the following errors:

test.hs:5:54: error:
    • Couldn't match expected type ‘a0 -> [[Char]]’
                  with actual type ‘[[Char]]’
    • Possible cause: ‘map’ is applied to too many arguments

I thought that these dots meant exactly the same as the brackets: function composition. Why do the brackets work, whereas the dots give me a type-related error? Any help would be greatly appreciated!


Solution

  • Brackets don't mean function composition. They just mean “group this subexpression”. Of course you can use them to make a chain of function compositions: the following defines c as a composition of the functions f, g and h

    c x = f (g (h x))
    

    This could also be written as:

    c = f . g . h
    

    Thus, you could write

    main = print $ c list
     where c = intercalate " something " . reverse . map reverse
    

    But if you then inline c again, you need to be careful not to mess up the parsing rules: just writing list to the right of that composition chain will not do, because function application binds more tightly than any infix operator (including ., though that is in fact the tightest infix operator). I.e.,

    intercalate " something " . reverse . map reverse list
    

    is actually parsed as

    (intercalate " something ") . (reverse) . (map reverse list)
    

    But that's not what you wanted. You need to make sure that list is actually the argument to the entire composition chain, not just its last element; the preferred way to do that is with the $ operator:

    intercalate " something " . reverse . map reverse $ list
    

    $ has the lowest prevedence, thus this is parsed as

    ((intercalate " something ") . (reverse) . (map reverse)) (list)
    

    Alternatively, you can apply map reverse to list right away – this by itself isn't wrong, just the result isn't part of the composition chain anymore:

    intercalate " something " . reverse $ map reverse list