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.
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?
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
Dot operator in Haskell is completely similar to mathematics composition: f{g(x)} where g() is a function and its output used as an input of another function, that is, f(). The result of . (dot) operator is another function (or lambada) that you can use and call it.
In general terms, where f and g are functions, (f . g) x means the same as f (g x). In other words, the period is used to take the result from the function on the right, feed it as a parameter to the function on the left, and return a new function that represents this computation."
Essentially, a >> b can be read like "do a then do b , and return the result of b ". It's similar to the more common bind operator >>= .
In Haskell we have or operator to compare the values of the variable, this operator also comes under the lexical notation of the Haskell programming language. This operator works in the same way as any other programming language, it just returns true or false based on the input we have provided.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With