2

I'm trying to learn haskell after years of OOP. I'm reading Happy Haskell. It provides this code:

plus :: Int -> Int -> Int
plus x y = x + y

plus' :: Int -> Int -> Int
plus' = \x -> \y -> x + y

increment :: Int -> Int
increment = plus 1

increment' :: Int -> Int
increment' = (\x -> \y -> x + y) 1

I understand how plus and plus' work (they're the same, different syntax). But increment, I don't get.

increment :: Int -> Int

means it takes an int, and returns an int, right? But right after that, the actual function is:

increment = plus 1

Question:

Where is the integer value increment takes? Shouldn't there be an x or something on the right of the = sign, to signify the integer value the function takes as input? Something like:

increment _ = plus 1 x

Edit: Also, shouldn't the definition of increment be Int -> (Int -> Int) since it takes an int and passes it to a function that takes an int and returns and int?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
SilentDev
  • 20,997
  • 28
  • 111
  • 214
  • Not sure if you are aware but the example shows you about 3 or 4 concepts at once: partial application, point free style, lambdas, currying/uncurrying. These are not difficult concepts but might be a bit too much for beginners when presented all together. – Elmex80s Feb 27 '19 at 08:11

4 Answers4

4

It would be increment x = plus 1 x, but generally foo x = bar x is the same thing as foo = bar because if f is a function that returns g(x) whenever called with any argument x, then f is the same function as g. So increment = plus 1 works just as well.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • Got it. But then since `x` is omitted, the only way a user can tell that `increment` takes only 1 input is by reading it's description (which isn't mandatory to be there), and if the description is not there, then to fully understand how `plus` works? That's a downfall in not specifying how many arguments there can be, right? – SilentDev Feb 27 '19 at 02:00
  • And btw, should the definition of increment be `Int -> (Int -> Int)` since it takes an `int` and passes it to a function that takes an `int` and returns and `int`? – SilentDev Feb 27 '19 at 02:05
  • 2
    @user2719875 You can look at its type (which GHCi or possibly your code editor will be able to tell you even if there's no type signature) to see how many arguments it takes. – sepp2k Feb 27 '19 at 02:05
  • 2
    "should the definition of increment be Int -> (Int -> Int) since it takes an int and passes it to a function that takes an int and returns and int" No. It takes an int and returns an int, so `Int -> Int` is the correct type signature. The fact that it calls another function to return the int, doesn't change the type. The type doesn't tell you how a function is implemented, only what it does (type-wise). So "takes an int, returns an int" means "Int -> Int" regardless of how the function is implemented. – sepp2k Feb 27 '19 at 02:07
  • "But then since `x` is omitted, the only way a user can tell that `increment` takes only 1 input is by reading it's description" After getting more used to the language and this kind of notation in particular, you get the hang of looking at how many arguments are provided in the definition, and inferring from that whether the function takes any implicit ones. But in general, Haskell functions are written with explicit type signatures, and it's easy to tell just from that. – DarthFennec Feb 27 '19 at 18:24
  • 2
    "should the definition of increment be `Int -> (Int -> Int)`?" The definition of `increment` is `plus 1`, and the type of `plus` is `Int -> (Int -> Int)`. That first `Int` is the `1`, so the remaining type of `increment` is `Int -> Int`. – DarthFennec Feb 27 '19 at 18:26
4

Partial Application

In Haskell, you can have currying and partial application of functions. Have a look a the Haskell Wiki: Partial Application

In particular, if you look at the type signature of any function, there's no real distinction between its inputs (arguments) and its outputs, and this is because really your function plus :: Int -> Int -> Int is a function that, when given an Int, will return another function which itself takes the remaining arguments and returns the int: Int -> Int. This is called partial application

This means that when you call increment = plus 1 you are saying that increment is equal to -- remember the partial application -- a function (returned by plus 1) which itself take an integer and returns an integer.

As Haskell is a functional programming language, everything with an equal is not an assignment, but more like a definition, so an easy way to understand partial application is really to follow the equal signs:

increment = plus 1 = 
            plus 1 y = 1 + y

Main uses

As you can see, partial application can be used to defined more specific functions, like add 1 to a number which is more specific than just add two numbers. It also allows more use of point-free style, where you concatenate more than one function.

Also note that with infix functions lke (+), you can partially apply to either the left or the right, which can be useful for non-commutative functions, for example

divBy2 :: Float -> Float
divBy2 = (/2)

div2by :: Float -> Float
div2by = (2/)

Prelude> divBy2 3
1.5
Prelude> div2by 2
1.0
Lorenzo
  • 2,160
  • 12
  • 29
  • Thank you. You mentioned that we can follow the equal sign `increment = plus 1 = plus 1 y = 1 + y` and you mentioned infix functions like `(+)`. How would we write `increment` so that it is `y + 1` after following equal sign, instead of `1 + y`? Asking because `increment = 1 plus` does not seem to work since we are supposed to apply the `plus` function to `1`, not the other way. – SilentDev Feb 27 '19 at 02:17
  • 1
    Strictly speaking, there is no partial application of functions in Haskell, because *every* function takes exactly one argument when called. There is just application. `plus 3 4` is evaluated as `(plus 3) 4`: apply the function returned by `plus 3` to the argument `4`. – chepner Feb 27 '19 at 14:26
  • 1
    It just *looks* like what languages providing multi-parameter functions would call partial application. – chepner Feb 27 '19 at 14:28
2

This is because all functions in Haskell are implicitly curried. As such, there is no distinction between a function which returns a function taking an argument and a function which takes two arguments returning a value (both have the type a -> a -> a). So calling plus (or any other function) with too few arguments simply returns a new function with the already-given arguments applied. In most languages, this would be an argument error. See also point-free style.

Haskell type signatures are right-associative, so a -> a -> a -> a is equivalent to a -> (a -> (a -> a)).

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
  • Ok thank you. One more thing: shouldn't the definition of increment be `Int -> (Int -> Int)` since it takes an `int` and passes it to a function that takes an `int` and returns and `int`? – SilentDev Feb 27 '19 at 02:07
  • 1
    To answer my question in the comments: The defintion of `increment` is `Int -> Int` because `plus 1` takes a argument already (1), so `increment` technically is just a function returned by `plus`, that is waiting for 1 argument (i.e. this: `\y -> 1 + y`). Is my understanding correct? – SilentDev Feb 27 '19 at 02:09
  • Yes. Any function which takes N arguments and is called with M arguments (where M < N) returns a new function which takes N-M arguments. It’s similar to partial function application (e.g. [`functools.partial` in Python](https://docs.python.org/3.7/library/functools.html#functools.partial)). – Andrew Marshall Feb 27 '19 at 02:12
2

The examples of plus and plus' are instructive. You see how the latter seems to have no arguments, at least on the left of the equals sign:

plus' :: Int -> Int -> Int
plus' = \x -> \y -> x + y

Let's make another pair of versions of increment (I'll name them after "bumping" a number—by 1) that go halfway to the final versions you gave:

bump :: Int -> Int
bump y = 1 + y

bump' :: Int -> Int
bump' = \y -> 1 + y

The analogy between these two definitions is just like the one between plus and plus', so these should make sense, including the latter even though it has no formal arguments on the left-hand side of the equal sign.

Now, your understanding of bump', is exactly the same understanding you need to understand increment' as you gave it in your question! In fact, we're defining bump' to be equal to something which is exactly what increment' is equal to.

That is (as we'll see shortly), the right-hand side of bump''s definition,

\y -> 1 + y

is something that is equal to

plus 1

The two notations, or expressions, are two syntactic ways of defining "the function that takes a number and returns one more than it."

But what makes them equal?! Well, (as other answerers have explained) the expression plus 1 is partially applied. The compiler, in a way, knows that plus requires two arguments (it was declared that way after all) and so when it appears here applied to just one argument, the compiler knows that it's still waiting for one more. It represents that "waiting" by giving you a function, saying, if you give one more argument, whether now or later, doing so will make this thing fully applied and the program will actually jump to the function body of plus (thus computing x + y for the two arguments that were given, the literal 1 from the expression plus 1 and the "one more" argument given later)

A key part of the joy and the value of Haskell is thinking about functions as things in themselves, which can be passed around and very flexibly transformed from one into another. Partial application is just such a way of transforming one thing (a function with "too many arguments", when you want to fix the value of the extras) into a function of "just the right many." You might pass the partially-applied function to an interface which expects a specific number of arguments. Or you might simply want to define multiple, specialized, functions based on one general definition (as we can define the general plus and more specific functions like plus 1 and plus 7).

ezrakilty
  • 21
  • 3