Dot Operator in Haskell: need more explanation
I'm trying to understand what the dot operator is doing in this Haskell code:
sumEuler = sum . (map euler) . mkList
The entire source code is below.
My understanding
The dot operator is taking the two functions sum
and the result of map euler
and the result of mkList
as the input.
But, sum
isn't a function it is the argument of the function, right? So what is going on here?
Also, what is (map euler)
doing?
Code
mkList :: Int -> [Int]
mkList n = [1..n-1]
euler :: Int -> Int
euler n = length (filter (relprime n) (mkList n))
sumEuler :: Int -> Int
sumEuler = sum . (map euler) . mkList
Put simply, .
is function composition, just like in math:
f (g x) = (f . g) x
In your case, you are creating a new function, sumEuler
that could also be defined like this:
sumEuler x = sum (map euler (mkList x))
The style in your example is called "point-free" style -- the arguments to the function are omitted. This makes for clearer code in many cases. (It can be hard to grok the first time you see it, but you will get used to it after a while. It is a common Haskell idiom.)
If you are still confused, it may help to relate .
to something like a UNIX pipe. If f
's output becomes g
's input, whose output becomes h
's input, you'd write that on the command-line like f < x | g | h
. In Haskell, .
works like the UNIX |
, but "backwards" -- h . g . f $ x
. I find this notation to be quite helpful when, say, processing a list. Instead of some unwieldy construction like map (\x -> x * 2 + 10) [1..10]
, you could just write (+10) . (*2) <$> [1..10]
. (And, if you want to only apply that function to a single value; it's (+10) . (*2) $ 10
. Consistent!)
The Haskell wiki has a good article with some more detail: http://www.haskell.org/haskellwiki/Pointfree
The . operator composes functions. For example,
a . b
Where a and b are functions is a new function that runs b on its arguments, then a on those results. Your code
sumEuler = sum . (map euler) . mkList
is exactly the same as:
sumEuler myArgument = sum (map euler (mkList myArgument))
but hopefully easier to read. The reason there are parens around map euler is because it makes it clearer that there are 3 functions being composed: sum, map euler and mkList - map euler is a single function.
sum
is a function in the Haskell Prelude, not an argument to sumEuler
. It has the type
Num a => [a] -> a
The function composition operator .
has type
(b -> c) -> (a -> b) -> a -> c
So we have
euler :: Int -> Int
map :: (a -> b ) -> [a ] -> [b ]
(map euler) :: [Int] -> [Int]
mkList :: Int -> [Int]
(map euler) . mkList :: Int -> [Int]
sum :: Num a => [a ] -> a
sum . (map euler) . mkList :: Int -> Int
Note that Int
is indeed an instance of the Num
typeclass.
The . operator is used for function composition. Just like math, if you have to functions f(x) and g(x) f . g becomes f(g(x)).
map is a built-in function which applies a function to a list. By putting the function in parentheses the function is treated as an argument. A term for this is currying. You should look that up.
What is does is that it takes a function with say two arguments, it applies the argument euler. (map euler) right? and the result is a new function, which takes only one argument.
sum . (map euler) . mkList is basically a fancy way of putting all that together. I must say, my Haskell is a bit rusty but maybe you can put that last function together yourself?