4

I have function add which I apply partially to create a new function addOne.

add :: Int -> (Int -> Int)
add x y = x + y

addOne can be defined with explicit parameter

addOne :: Int -> Int
addOne y = add 1 y

or without explict parameter

addOne :: Int -> Int
addOne = add 1

I have four questions:

  1. Why can I define the new function without explicit parameter?
  2. Is there any difference between these two definitions?
  3. When do I know when I can define function without a parameter?
  4. Which definition is preferred and when?
Rene
  • 289
  • 1
  • 4
  • 8
  • 3
    you should have a look at *curried functions* and *partial application* : http://www.haskell.org/haskellwiki/Currying – Random Dev Oct 23 '14 at 16:51
  • Consider the case `strTake :: Int -> String -> String`; `strTake = take`. All this is doing is making an alias for `take` that only works on `String`s instead of any kind of list. You don't need to define the parameters, you're just setting one value equal to another value. Then you can consider something like `take5 :: [a] -> [a]`; `take5 = take 5`. – bheklilr Oct 23 '14 at 16:59
  • yet another tutorial: http://learnyouahaskell.com/higher-order-functions – GeneralBecos Oct 23 '14 at 17:00
  • Note also that because `->` is right associative, `Int -> (Int -> Int)` is the same as `Int -> Int -> Int`. Unlike some other languages, there's no distinction between the two types in Haskell. As others have noted, whether you *implement* the function as an explicit lambda or not sometimes does make a difference for optimization or the monomorphism restriction. – Christian Conkle Oct 23 '14 at 18:10

2 Answers2

5
  1. Because addOne y = add 1 y means addOne = \y -> add 1 y, and \x -> f x is always just f. This is called eta equivalence. So addOne = add 1.

  2. No

  3. Always. Function parameters are just syntactic sugar for lambdas:

    add :: Int -> Int -> Int
    add = \x y -> x + y
    

    Whether you can remove the variable binding completely is a different matter.

  4. It's always nice to "eta reduce" (that is, remove the rightmost bound variable in a function binding when it matches a function application in the bound expression) when you can, as it avoids introducing a superfluous name.

Tom Ellis
  • 9,224
  • 1
  • 29
  • 54
  • 1
    I will add that some editors support `hlint` integration, which is great for telling you when you can eta reduce, saving you the effort of having to find all those instances yourself. – bheklilr Oct 23 '14 at 17:25
  • Seconded, `hlint` is great for that. Eta reduction is fun! – Tom Ellis Oct 23 '14 at 17:31
  • 4
    As for question 2, there is a difference to the GHC optimizer, but that's probably something we shouldn't confuse people with here. – Thomas M. DuBuisson Oct 23 '14 at 18:04
4

One of the basic concepts in functional programming that you'll need to learn to use Haskell is that functions are just a kind of value, definitions just name things. It's not like procedural languages where there's a sharp distinction made between functions and variables and function definitions are completely different from variable definitions.

So a variable definition like

addOne :: Int -> Int
addOne = add 1

is just adding a name for the expression add 1, so you can refer to it as addOne. It's identical to a variable declaration.[1] The fact that the value of that variable is a function is almost incidental, from Haskell's perspective.

Your add definition:

add :: Int -> (Int -> Int)
add x y = x + y

is also a variable definition. It's a bit of syntax sugar Haskell provides for:

add :: Int -> Int -> Int
add = \ x -> \ y -> x + y

on the theory that it's easier to read. But it's still just sugar; you never need it (except see [1] below) like you would in other languages.

[1]: The dreaded monomorphism restriction also comes into play here. The idea is just: in a function definition, the RHS will be executed by the computer many times (as many times as you call the function). You are probably aware of this from other languages. In a monomorphic variable definition, the RHS will be executed at most once, which is also similar to how other languages work. However, a polymorphic variable often ends up acting like a function definition, with the RHS being executed as many times as the value of the variable is accessed. So Haskell dis-allows polymorphic definitions unless you have a polymorphic type signature (so you say "I know what I'm doing, allow this variable to be polymorphic") or you have arguments on the left-hand side (so it "looks like" the RHS should be executed many times).

Jonathan Cast
  • 4,569
  • 19
  • 34