Composition and the $ Operator
Prerequisites: Currying and partial application
Two small operators appear in almost every Haskell program.
$: application without parentheses
f $ x means f x, but $ has the lowest precedence of all
operators, so everything to its right is evaluated first. It replaces a
pair of parentheses that would extend to the end of the line:
main :: IO ()
main = do
print (sum (map (* 2) (filter even [1 .. 10])))
print $ sum $ map (* 2) $ filter even [1 .. 10]
60
60
Both lines are the same program. Read the second from right to left: keep the even numbers, double them, add them up, print.
.: function composition
(f . g) x means f (g x) — the dot combines two functions into a new
one, just like ∘ in mathematics:
import Data.Char (toUpper)
shout :: String -> String
shout = map toUpper . reverse
countEvens :: [Int] -> Int
countEvens = length . filter even
main :: IO ()
main = do
putStrLn (shout "hello")
print (countEvens [1, 2, 3, 4, 6])
print ((negate . abs) 7, (show . (* 2)) 21)
OLLEH
3
(-7,"42")
Composition builds a pipeline of functions that data flows through
from right to left. Combined with $, a common style is:
main :: IO ()
main = print . sum . map (* 2) . filter even $ [1 .. 10]
60
& for left-to-right
If you prefer reading left to right, Data.Function provides &
(reverse application):
import Data.Function ((&))
main :: IO ()
main = [1 .. 10] & filter even & map (* 2) & sum & print
60
Common mistakes
print . sum [1, 2]fails: composition joins functions, butsum [1, 2]is already a number. Writeprint . sum $ [1, 2]orprint (sum [1, 2]).- Overusing point-free style.
f = (. g) . (+)is clever but unreadable; name the arguments when that's clearer.