Boolean condition

Boolean condition #

Pattern matching has limitations.

Examples. No pattern can match:

  • integers greater than 2,
  • odd integers,
  • etc.

For more expressive conditions, programming language usually rely on Boolean expressions.

In Haskell #

Boolean expression #

We have seen how to define a Boolean operator (precisely, the Boolean operator “AND”) with patterns in Haskell.

This is not necessary: Haskell provides native implementations, similar to their counterparts in Java (and many programming languages). They include:

  • conjunction, noted &&,
  • disjunction, noted ||,
  • negation, noted not.

Examples.

  • 3 < 2 && 3 > 5 evaluates to False
  • not (3 > 5 || True) evaluates to False

Guards #

Syntax. A guard is a Boolean expression preceded with a |.

Similarly to patterns, guards let us define a function on a case-by-case basis. The first guard that evaluates to True determines the return value.

Example. The following function maps an integer to its absolute value:

abs :: Int -> Int
        -- if x >= 0, then return it
abs x | x >= 0 = x
        -- if x < 0, then return - x
      | x < 0 = - x

There are two guard in this definition:

  • | x >= 0
  • | x < 0

Note. The function above is equivalent to

abs :: Int -> Int
abs x | x >= 0 = x
      | True = - x

Syntax. Haskell provides the keyword otherwise as an alias for True.

Example. The function above can be written

abs :: Int -> Int
abs x | x >= 0 = x
      | otherwise = - x

Simplify the following function.

dummyComp :: Int -> Int -> Bool
dummyComp x y | x <= y = True
              | x < y || x + 1 <= y = False
              | otherwise = True
dummyComp :: Int -> Int -> Bool
dummyComp _ _ = True

Syntax. A guard can be used after a pattern.

Example.

-- Maps a number n to the nth Fibonacci number
fib :: Int -> Int
fib 1 = 1                               -- pattern only
fib n | n > 1 = fib (n-1) + fib (n-2)   -- pattern + guard
      | otherwise = 0

To go further. A pattern can also be used inside a guard, with a left arrow (<-).

This can be helpful to pattern match the result of a function call. We will not cover this syntax here. Instead, we already introduced pattern matching within a case expression or an alias definition, which can serve the same purpose.

Conditional statement #

Haskell also supports “if/then/else” expressions, similar to their counterparts in imperative languages (like Java).

Example. The following function maps:

  • a (strictly) positive integer to 1,
  • 0 to 0, and
  • a negative integer to -1.
signum :: Int -> Int
signum x =
  if x > 0
    then 1
    else
      if x == 0
        then 0
        else -1

Warning. As opposed to Java, the else is mandatory.

Example. The following function definition is not valid syntactically:

invalid:: Int -> Int
invalid _ = if True then 0  -- missing `else`

In Java #

You should already be familiar with Boolean expressions in Java, as well as their usage in:

  • conditional statements (if (<expression>){<instructions>} else {<instructions>}),
  • loop termination condition (while (<expression>){<instructions>}),
  • switch statements,
  • etc.