Skip to content

If-Then-Else

In Python, we would probably implement our sign function like this:

def sign(x):
    if x > 0:
        return 1
    elif x == 0:
        return 0
    else:
        return -1

The Haskell code looks rather similar, only there is no elif or else if keyword—we need to achieve the same effect by nesting an if then else expression within the else branch of the outer if then else expression.

sign :: Int -> Int
sign x = if x > 0 then
            1
        else
            if x == 0 then
                0
            else
                -1

The other difference is that we don't have a return statement. Let me emphasize this one more time: returning a value is something we do in an imperative language, and there is no way to tell the computer to do anything in a functional language. We can only define the value of a function, here sign. You should read this as "sign x is 1 if x > 0. Otherwise, if x == 0, then sign x is 0. Otherwise, it is -1."

The indentation looks a bit excessive here. In fact, with the right indentation, we can make it look as if there were an else if keyword in Haskell:

sign :: Int -> Int
sign x =
    if x > 0 then
        1
    else if x == 0 then
        0
    else
        -1

As always, it may make things feel less abstract to play with this function in GHCi:

GHCi
>>> :{
  | sign x =
  |     if x > 0 then
  |         1
  |     else if x == 0 then
  |         0
  |     else
  |         -1
  | :}
>>> sign 5
1
>>> sign (-10)
-1
>>> sign 0
0

Remember that you can indent fairly freely, as long as the indentation levels indicate the block structure of your code, as discussed here.

You may have noticed that I called if then else an "if then else expression". You're probably more used to calling it an if statement. The distinction is important. A statement states something we should do. An expression defines a value. This has an important consequence. In an imperative language, we can have if statements without an else branch. If a condition holds, we need to do something. Otherwise, don't do anything. For example, in Python:

def abs(x):
    if x < 0:
        x = -x
    return x

In Haskell, if then else is an expression. If the condition after if does not hold, we cannot simply let the expression have no value. Thus,

The else branch is mandatory in Haskell.

A possible implementation of the abs function in Haskell is:

abs :: Int -> Int
abs x = if x < 0 then -x else x
GHCi
>>> abs x = if x < 0 then -x else x
>>> abs 5
5
>>> abs 0
0
>>> abs (-10)
10

Exercise

Haskell offers functions odd and even to test whether a number is odd or even. I've used these functions in some examples before.

GHCi
>>> even 5
False
>>> odd 5
True
>>> even 8
True
>>> odd 8
False

Let's pretend that these functions don't exist, and let's implement our own versions of these two functions, which we call myOdd and myEven to avoid name clashes with the two functions odd and even that do actually exist. We want these two functions to behave exactly like odd and even:

GHCi
>>> myEven 5
False
>>> myOdd 5
True
>>> myEven 8
True
>>> myOdd 8
False

To implement them, you'll find the mod operator and equality tests handy. Think about how you would implement the function myEven in Python:

def myEven(x):
    if x % 2 == 0:
        return True
    else:
        return False

Or, more succinctly:

def myEven(x):
    return x % 2 == 0

To complete this exercise, all you need to do is translate this function into Haskell, and then do the same with myOdd. Implement both versions, the one using if then else, and the one that directly returns the result of the comparison.

Solution

With if then else:

GHCi
>>> :{
  | myEven x =
  |     if x `mod` 2 == 0 then
  |         True
  |     else
  |         False
  | :}
>>> myEven 1
False
>>> myEven 2
True

Without if then else:

GHCi
>>> myEven x = x `mod` 2 == 0
>>> myEven 1
False
>>> myEven 2
True

And, as good software engineers, we won't replicate the logic but simply use that a number is odd if it is not even:

GHCi
>>> myOdd x = not (myEven x)
>>> myOdd 1
True
>>> myOdd 2
False

Given my dislike for parentheses, and as an illustration of the function application operator ($), here is a version using this operator. Remember, not $ myEven x applies not to myEven x, so it's exactly the same as not (myEven x):

GHCi
>>> myOdd x = not $ myEven x
>>> myOdd 1
True
>>> myOdd 2
False

Here's an even shorter version using function composition. Instead of defining what the value of myOdd is on a given argument x, as the previous two definitions do, we focus on how the function myOdd itself can be built from existing functions. To produce the result of myOdd on an argument x, we need to apply myEven to x and then apply not to the result of this application of myEven. That's nothing but the composition of not and myEven, so we can define myOdd = not . myEven:

GHCi
>>> myOdd = not . myEven
>>> myOdd 1
True
>>> myOdd 2
False