Defining functions

Function declarations in Haskell rely less on parentheses and more on whitespace relative to many other languages.

Here we define a function which returns a number that is one greater than its argument.

A basic function definition begins with:

  • the name of the function (in this case, next), followed by a space;
  • the parameters (here just one parameter, x).

Function and parameter names begin with lowercase letters.

next x = x + 1

If there is more than one parameter, they are separated with whitespace. This hypotenuse function requires two arguments, x and y.

hypotenuse x y = sqrt (x^2 + y^2)

In many cases, annotating a function definition with its type is unnecessary. In greet, the compiler can infer that since we are concatenating the argument called name with a string, then the type of name must be String and the result returned from the function is also a String.

greet name = "hello" ++ " " ++ name

But explicit type annotations are often useful.

  • The function name is followed by ::
  • the type of each parameter is given followed by an arrow;
  • the final type is the return type.

greet2 takes one String argument, referred to in the function definition by the parameter name, and returns one String result.

Type names always begin with an uppercase letter.

greet2 :: String -> String
greet2 name = "hello" ++ " " ++ name

Functions can return multiple values. The greetNext function returns a tuple of two values:

  1. next x
  2. greet (show (next x))
greetNext x = (next x, greet (show (next x)))

Pattern matching is another useful way to define functions. Here we define a function called hello that has three cases:

  1. the first applies only when the argument is "Olafur";
  2. the second when the argument is "Rocamadour";
  3. the third is a fallback that matches anything else.
hello :: String -> String
hello "Olafur" = "hello, Olafur!"
hello "Rocamadour" = "hey!"
hello x = greet x
main =
  do

The show function converts values that are not strings into strings that we can print to the screen or otherwise use in string-manipulating functions.

    putStrLn (show (next 4))
    putStrLn (show (next (next 4)))

    putStrLn (show (hypotenuse 3 4))

Since greet and greet2 already return String values, we do not need to use show here.

    putStrLn (greet "world")
    putStrLn (greet2 "world")

We can treat the result from greetNext as a single value.

    putStrLn (show (greetNext 7))

Or we can immediately destructure it and give names (x and y) to its two constituent parts.

    let (x, y) = greetNext 7
    putStrLn (show x)
    putStrLn y

    putStrLn (hello "Olafur")
    putStrLn (hello "Rocamadour")
    putStrLn (hello "Jane")
$ runhaskell functions.hs
5
6
5.0
hello world
hello world
(8,"hello 8")
8
hello 8
hello, Olafur!
hey!
hello Jane