5
votes

Consider following function definition in ghci.

let myF = sin . cos . sum 

where, . stands for composition of two function (right associative). This I can call

myF [3.14, 3.14]

and it gives me desired result. Apparently, it passes list [3.14, 3.14] to function 'sum' and its 'result' is passed to cos and so on and on. However, if I do this in interpreter

let myF y = sin . cos . sum y 

or

let myF y = sin . cos (sum y) 

then I run into troubles. Modifying this into following gives me desired result.

let myF y = sin . cos $ sum y 

or

let myF y = sin . cos . sum $ y 

The type of (.) suggests that there should not be a problem with following form since 'sum y' is also a function (isn't it? After-all everything is a function in Haskell?)

let myF y = sin . cos . sum y -- this should work?

What is more interesting that I can make it work with two (or many) arguments (think of passing list [3.14, 3.14] as two arguments x and y), I have to write the following

let (myF x) y = (sin . cos . (+ x)) y 
myF 3.14 3.14 -- it works! 
let myF = sin . cos . (+) 
myF 3.14 3.14 -- -- Doesn't work!

There is some discussion on HaskellWiki regarding this form which they call 'PointFree' form http://www.haskell.org/haskellwiki/Pointfree . By reading this article, I am suspecting that this form is different from composition of two lambda expressions. I am getting confused when I try to draw a line separating both of these styles.

3
It is not true that "everything is a function in Haskell." Conal Elliot has a great discussion of this on his blog.Antal Spector-Zabusky

3 Answers

10
votes

Let's look at the types. For sin and cos we have:

cos, sin :: Floating a => a -> a

For sum:

sum :: Num a => [a] -> a

Now, sum y turns that into a

sum y :: Num a => a

which is a value, not a function (you could name it a function with no arguments but this is very tricky and you also need to name () -> a functions - there was a discussion somewhere about this but I cannot find the link now - Conal spoke about it).

Anyway, trying cos . sum y won't work because . expects both sides to have types a -> b and b -> c (signature is (b -> c) -> (a -> b) -> (a -> c)) and sum y cannot be written in this style. That's why you need to include parentheses or $.

As for point-free style, the simples translation recipe is this:

  • take you function and move the last argument of function to the end of the expression separated by a function application. For example, in case of mysum x y = x + y we have y at the end but we cannot remove it right now. Instead, rewriting as mysum x y = (x +) y it works.
  • remove said argument. In our case mysum x = (x +)
  • repeat until you have no more arguments. Here mysum = (+)

(I chose a simple example, for more convoluted cases you'll have to use flip and others)

7
votes

No, sum y is not a function. It's a number, just like sum [1, 2, 3] is. It therefore makes complete sense that you cannot use the function composition operator (.) with it.

Not everything in Haskell are functions.

4
votes

The obligatory cryptic answer is this: (space) binds more tightly than .

Most whitespace in Haskell can be thought of as a very high-fixity $ (the "apply" function). w x . y z is basically the same as (w $ x) . (y $ z)

When you are first learning about $ and . you should also make sure you learn about (space) as well, and make sure you understand how the language semantics implicitly parenthesize things in ways that may not (at first blush) appear intuitive.