6

Why it's possible to reduce function in Haskell:

calculate :: Integer -> Integer -> Integer
calculate a b = f 1 a b
  where
     f :: Integer -> Integer -> Integer -> Integer
     f a b c = a + b

into something:

calculate :: Integer -> Integer -> Integer
calculate = f 1
  where
     f :: Integer -> Integer -> Integer -> Integer
     f a b c = a + b

I just need some guidance, any resources where I can find the answer and read more about it would be really helpful.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555

2 Answers2

8

In Haskell there are no functions that take more than one parameter. All functions take exactly one parameter.

So if you have a function like add :: Int -> Int -> Int, then this is actually short for add :: Int -> (Int -> Int).

Why are the brackets important? Because they show how this concept works. If we have this function add, then we can make a function application with for example 14, then we construct a new function, like:

add14 :: Int -> Int
add14 = add 14

so that means that we now have a function that takes again one parameter (here an Int), and now it will produce another Int, it does that by adding 14 to it, so add14 25 will result in 39.

If you write add 14 25, this thus is not a function application with two parameters, what you actually wrote is:

-- add 14 25 is equivalent to
(add 14) 25

You thus first make a call with 14, and the you make a call with the function that comes out of this, and 25 as the parameter.

Why is this important? Because it means that if you thus write

calculate = f 1

it means that your f 1, constructs a function, a function with signature Int -> (Int -> Int). Creating parameters in the head of calculate, and adding these to the end of f 1, thus makes no sense: you already constructed a function that takes such parameters anyway. So it only introduces noise.

In lambda calculus, the rewrite rule where one rewrites λ x . f x to just f is (and vice versa) is called η-conversion [wiki]. In Haskell it comes down to rewriting:

f x = g x

to:

f = g

Operators are no different. In fact if you write a + b in Haskell, you wrote (+) a b, with (+) a function, or more verbose ((+) a) b.

The f in the where clause:

f a b c = a + b

can for example get converted to:

f = (.) ((.) const) (+)
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • 1
    Really appreciate for your detailed answer, it couldn't have been any simpler explained when that :) – Mantas Savaniakas Sep 29 '18 at 21:37
  • 1
    It's probably helpful to note as well that a definition like `foo a b = a + b` is really just syntactic sugar for `foo = \a -> \b -> a + b`: just a named assigned to an otherwise anonymous function defined by a lambda expression. – chepner Oct 01 '18 at 13:41
  • @chepner: good idea! I've added this at the end of the answer. – Willem Van Onsem Oct 01 '18 at 14:07
3

It's called eta reduction.

You might also think about it in terms of partial application.

> calculate :: Integer -> Integer -> Integer
> f :: Integer -> Integer -> Integer -> Integer
> (f 1) :: Integer -> Integer -> Integer

Similar question - What does eta reduce mean in the context of HLint