Dot Operator in Haskell: need more explanation

62,277

Solution 1

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

Solution 2

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.

Solution 3

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.

Solution 4

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?

Solution 5

Dot Operator in Haskell

I'm trying to understand what the dot operator is doing in this Haskell code:

sumEuler = sum . (map euler) . mkList

Short answer

Equivalent code without dots, that is just

sumEuler = \x -> sum ((map euler) (mkList x))

or without the lambda

sumEuler x = sum ((map euler) (mkList x))

because the dot (.) indicates function composition.

Longer answer

First, let's simplify the partial application of euler to map:

map_euler = map euler
sumEuler = sum . map_euler . mkList

Now we just have the dots. What is indicated by these dots?

From the source:

(.)    :: (b -> c) -> (a -> b) -> a -> c
(.) f g = \x -> f (g x)

Thus (.) is the compose operator.

Compose

In math, we might write the composition of functions, f(x) and g(x), that is, f(g(x)), as

(f ∘ g)(x)

which can be read "f composed with g".

So in Haskell, f ∘ g, or f composed with g, can be written:

f . g

Composition is associative, which means that f(g(h(x))), written with the composition operator, can leave out the parentheses without any ambiguity.

That is, since (f ∘ g) ∘ h is equivalent to f ∘ (g ∘ h), we can simply write f ∘ g ∘ h.

Circling back

Circling back to our earlier simplification, this:

sumEuler = sum . map_euler . mkList

just means that sumEuler is an unapplied composition of those functions:

sumEuler = \x -> sum (map_euler (mkList x))
Share:
62,277
cbrulak
Author by

cbrulak

c++,php,perforce,haskell,python,ROR, recently android and iOS SOreadytohelp

Updated on September 05, 2020

Comments

  • cbrulak
    cbrulak almost 4 years

    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
    
  • SwiftsNamesake
    SwiftsNamesake over 6 years
    Tiny quibble: the first code snippet isn't actually valid Haskell.
  • user234461
    user234461 almost 6 years
    @SwiftsNamesake For those of us who aren't fluent in Haskell, do you just mean that the single equals sign is not meaningful here? (So the snippet should have been formatted "f (g x) = (f . g) x"?) Or something else?
  • SwiftsNamesake
    SwiftsNamesake almost 6 years
    @user234461 Exactly, yeah. You'd need == instead if you wanted valid standard Haskell.
  • Tarick Welling
    Tarick Welling about 4 years
    That little snippet up top is just gold. Like the other answers here are correct but that snippet just clicked directly intuitively in my head which made it unnecessary to read the rest of your answer.
  • Snowmanzzz
    Snowmanzzz over 2 years
    sum :: Num a => [a ] -> a where does this line come?
  • Geno Chen
    Geno Chen about 2 years