3

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 =
Will Ness
  • 70,110
  • 9
  • 98
  • 181
moliverac8
  • 63
  • 2
  • 7

2 Answers2

5

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.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • that's not what is asked in the question, though. – Will Ness Sep 29 '19 at 13:25
  • @WillNess: it asks to make a curried function, not a point-free one. – Willem Van Onsem Sep 29 '19 at 13:26
  • look at the types. :) it's not the first time an asker is confused about the terminology. – Will Ness Sep 29 '19 at 13:26
  • @WillNess: I honestly don't see what the types do here :) especially since Haskell's syntax already is curried, and making something point-free should of course not alter the types. – Willem Van Onsem Sep 29 '19 at 13:36
  • the types of `prod'` and `prod''` are both `Integer->Integer->Integer->Integer->Integer`. the types of your partially applied examples are different. so I interpreted this as OP's confusion about terminology. – Will Ness Sep 29 '19 at 13:38
  • Furthermore I don't see any proof of confusion with point-free either. It is not strange that people that come from for example JavaScript, think that `prod x y z t = ...` is equivalent to `func prod(x, y, z, y) { ... }`, and thus aim to make something like `prod(x) { ... }` to do it. My Occam's razor sees that as a more logical explanation (since Haskell's syntax is a bit "overconvenient" by some). – Willem Van Onsem Sep 29 '19 at 13:38
  • @WillNess: of course they are, these are only to demonstrate that we can do currying. It thus demonstrates that we can call `prod 2` in the first place. Since in JavaScript (to continue on the example), calling `prod(2)` on a `func prod (x, y, z, t) { ... }` would usually end up with an error. – Willem Van Onsem Sep 29 '19 at 13:40
  • a nitpick: we don't "do" currying, we use it; Haskell *the language* is what's curried -- as you write, "all functions in Haskell are [already and irrevocably --wn] automatically curried". (the usual talk of `(a,b)->c ==> a->b->c` being "currying" just adds to the confusion.) – Will Ness Sep 29 '19 at 13:46
  • I get that Haskell actually only receives one argument and is returning functions until it reaches the last argument and then returns the actual value. That's why I thought about doing it like this but its not working and I can't understand why: prod'' :: Integer->Integer->Integer->Integer->Integer prod'' x y = (*) ((*) (x*y)) – moliverac8 Sep 29 '19 at 15:14
  • @moliverac8: well you can make it entirely "pointfree" with `prod = ((((*) .) . (*)) .) . (*)`, or with one argument, you can use `prod x = ((*) .) . (*) . (x *)`. – Willem Van Onsem Sep 29 '19 at 15:19
  • Hmm, I don't know what that term "pointfree" means. I'll investigate it – moliverac8 Sep 29 '19 at 15:24
  • @moliverac8 "[tag:pointfree]" means "without explicit arguments", like your `prod'` does not name `t`, and `prod''` does not name `t` nor `z`. see the [tag info page](https://stackoverflow.com/tags/pointfree/info). another name for it is "[tag:tacit-programming]" (judging from that tag's description). – Will Ness Sep 29 '19 at 15:34
4

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

              / 
         <-- *   
              \ /
               *
                \ /
                 *
                  \
Will Ness
  • 70,110
  • 9
  • 98
  • 181