2

Sorry for a newbie question. Suppose I have:

square :: Integer -> Integer
square x = x * x

I can do a function composition with parentheses:

sumsquares :: Integer -> Integer
sumsquares n = sum (map square [1..n])

But I'd like to use the dot notation. I'd thought this would work:

sum . map square [1..n]

but it throws an error:

• Couldn't match expected type ‘a0 -> t0 c0’ with actual type ‘[Integer]’
    • Possible cause: ‘map’ is applied to too many arguments
      In the second argument of ‘(.)’, namely ‘map square [1 .. n]’
      In the expression: sum . map square [1 .. n]
      In an equation for ‘sumsquares’:
          sumsquares n = sum . map square [1 .. n]

Neither works the (f.g)(x) logic, namely:

sum . square map [1..n]

What am I not getting, and what's the correct way of using the dot notation in this case?

Alex Kay
  • 260
  • 1
  • 14
  • 1
    Just to point out an extreme use of composition here, replace `[1..n]` with its functional equivalent `enumFromTo 1 n`. Then you can write `sum (map square (enumFromTo 1 n))`, which you can more easily(?) break down into `(sum . map square . enumFromTo 1) n` (`n` is passed to `enumFromTo 1`, whose return value is passed to `map square`, whose result is passed to `sum`. ) Then `sumsquares = sum . map square . enumFromTo 1` in point-free style. – chepner Sep 03 '22 at 14:18

1 Answers1

4

The (.) operator is for composing functions, not values, and what it returns is a function, not a value. This is because f . g is defined as the function which you get as running g on its input, then running f on the result of that:

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

Given this, let’s look at your non-working example again, which I will re-bracket to make the situation a little clearer:

sum . map square [1..n]
== (sum) . (map square [1..n])

Immediately, we can see a problem: map square [1..n] is not a function! This means we cannot run it on an input, which in turn means that sum . map square [1..n] would make no sense.

So what does make sense? Well, we could do sum . map square instead. Now both of the arguments of (.) are functions: sum sums its input (which must be a list of numbers), and map square applies square to each element of its input (which must be a list of numbers). Thus, their composition makes sense, the types all line up, and we can use (.) with no problems. We can then feed an input to this composite function, by doing (sum . map square) [1..n].

Another option is to use ($) rather than (.). This operator is defined as follows:

f $ x = f x

That is, ($) simply applies the function on its left to the value on its right! This might seem a bit useless… but the nice thing about ($) is that it has a very low precedence, lower than almost anything else in Haskell. In short, this means that it has the effect of ‘bracketing’ everything to its right. Thus, it’s fine to write:

sum $ map square [1..n]

…because this is equivalent to:

sum (map square [1..n])

…which is what you’re trying to do. (For more on the difference between (.) and ($), I refer you to What is the difference between . (dot) and $ (dollar sign)? .)

bradrn
  • 8,337
  • 2
  • 22
  • 51
  • Thank you, this is a great explanation. I was slowly wisening up to this idea, hence my 2nd candidate (in my original post): ```sum . map square [1..n] ``` What I didn't expect is how things are parenthesized. I think it'd made more sense if it were ```(sum . square) map [1..n]```. But anyway, thanks again! – Alex Kay Sep 03 '22 at 04:08
  • 1
    @AlexKay You’re welcome! The general principle is that function application binds tighter than operator application — this is why you can do things like `length x - 1` and similar. – bradrn Sep 03 '22 at 04:12
  • This is a subtle point, thank you for pointing it out. I just learned about Haskell (been under a rock I guess!) and what appeals to me is its pure functional approach. So I don't indend on doing anything like ```length x - 1``` if I can help it. – Alex Kay Sep 03 '22 at 04:16
  • 1
    @AlexKay Why would you avoid expressions like `length x - 1`? Purely functional or not, all languages need *some* way of manipulating numbers! Admittedly that precise expression doesn’t come up all that much, but I could easily imagine doing similar things, like e.g. `map (\a -> maximum l - a) xs`. – bradrn Sep 03 '22 at 04:31
  • You are right. I really don't have any specific reasons to avoid number manipulation other than stylistic/philosophical. I am sure I'll need and use it. – Alex Kay Sep 03 '22 at 04:35
  • 1
    @AlexKay Perhaps you may be thinking of *index* manipulation specifically, which is rather common in imperative languages but almost absent from functional languages (thanks to the presence of `map`/`foldr`/`filter` etc., as well as the use of direct recursion). But numerical computations more generally are necessary everywhere. – bradrn Sep 03 '22 at 04:40
  • Yes, @bradrn, that's exactly it. Your insights have been spot on! – Alex Kay Sep 03 '22 at 04:49