Currying in Haskell with 2+ arguments

522 Views Asked by At

I'm starting to learn Haskell so I need to understand currying also (it's the first time I've seen this technique too). I think I get how it works in some cases where the currification only "eliminates" one of the parameters. Like in the next example where I'm trying to calculate the product of 4 numbers. This is the uncurried function:

prod :: Integer->Integer->Integer->Integer->Integer
prod x y z t = x * y * z * t

This is the curried function:

prod' :: Integer->Integer->Integer->Integer->Integer
prod' x y z = (*) (x*y*z)

But I don't understand how could I continue this dynamic and do for example the same function with only two arguments and so on:

prod'' :: Integer->Integer->Integer->Integer->Integer
prod'' x y =
2

There are 2 best solutions below

12
willeM_ Van Onsem On

This is the uncurried function:

prod :: Integer -> Integer -> Integer -> Integer -> Integer
prod x y z t = x * y * z * t

This is already a curried function. In fact all functions in Haskell are automatically curried. Indeed, you here wrote a function that looks like:

prod :: Integer -> (Integer -> (Integer -> (Integer -> Integer)))

Haskell will thus produce a function that looks like:

prod :: Integer -> (Integer -> (Integer -> (Integer -> Integer)))
prod = \x -> (\y -> (\z -> (\t -> x * y * z * t)))

Indeed, we can for example generate such function:

prod2 = prod 2

This will have type:

prod2 :: Integer -> (Integer -> (Integer -> Integer))
prod2 = prod 2

and we can continue with:

prod2_4 :: Integer -> (Integer -> Integer)
prod2_4 = prod2 4

and eventually:

prod2_4_6 :: Integer -> Integer
prod2_4_6 = prod2_4 6

EDIT

The function prod' with:

prod'' x y = (*) ((*) (x*y))

Since that means you multiply (*) (x*y) with the next parameter. But (*) (x*y) is a function. You can only multiply numbers. Strictly speaking you can make functions numbers. But the Haskell compiler thus complains that:

Prelude> prod'' x y = (*) ((*) (x*y))

<interactive>:1:1: error:
    • Non type-variable argument in the constraint: Num (a -> a)
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        prod'' :: forall a.
                  (Num (a -> a), Num a) =>
                  a -> a -> (a -> a) -> a -> a

It thus says that you here aim to perform an operation with a function a -> a as first operand, but that this function is not an instance of the Num typeclass.

0
Will Ness On

What you have is

prod x y z t  =    x * y * z * t
              =   (x * y * z) * t
              =  (*) (x * y * z) t

Hence by eta reduction (where we replace foo x = bar x with foo = bar)

prod x y z    =  (*) (x * y * z)
              =  (*) ( (x * y) * z )
              =  (*) ( (*) (x * y) z )
              =  ((*) . (*) (x * y)) z 

so that by eta reduction again,

prod x y      =   (*) . (*) (x * y)

Here (.) is the function composition operator, defined as

(f . g) x  =  f (g x)

What you're asking about is known as point-free style. "Point-free" means "without explicitly mentioning the [implied] arguments" ("point" is a mathematician's jargon for "argument" here).

"Currying" is an orthogonal issue, although Haskell being a curried language makes such definitions -- and partial application ones, shown in Willem's answer -- easier to write. "Currying" means functions take their arguments one at a time, so it is easy to partially apply a function to a value.

We can continue the process of pulling the last argument out so it can be eliminated by eta reduction further. But it usually rapidly leads to more and more obfuscated code, like prod = ((((*) .) . (*)) .) . (*).

That's because written code is a one-dimensional encoding of an inherently two-dimensional (or even higher-dimensional) computational graph structure,

  prod =           
                  /
                 *
                / \
               *   
              / \
         <-- *   
              \

You can experiment with it here. E.g., if (*) were right-associative, we'd get even more convoluted code

\x y z t -> x * (y * (z * t))
==
(. ((. (*)) . (.) . (*))) . (.) . (.) . (*)

representing just as clear-looking, just slightly rearranged, graph structure

              / 
         <-- *   
              \ /
               *
                \ /
                 *
                  \