Bool-urns

Roy: (Singing) We don’t need no education.
Moss: Yes you do. You’ve just used a double negative.
The IT Crowd, season 1, episode 4, 2006

Do you like samurai? Do you like pizza? Do you like cats? If you answer yes to all three questions, you might like Samurai Pizza Cats. Do you also like banana? Perhaps you might be interested in Bananya. Questions such as the above often require a yes/no answer. Yes means true, you prefer something. No means false, you dislike something. Questions or statements that can be answered with yes/no or true/false are said to have boolean values. Haskell models boolean values via the type Bool, which has two defined values: True and False.

What else can you do with boolean values? A simple operation is to use the function not to negate a boolean value, thus resulting in the opposite value. The result of not True is False because the opposite of True is False. As you might have already guessed, the result of not False is True.

Or

Ned: Who are you?
Villain: Big Mac… Beth.
Ned: To blow your head off or… not blow your head off—that is the question.
Reckless Kelly, 1993

Given a bunch of boolean values, you can use the Haskell boolean operators || and && to calculate boolean results.1 The operator || means “or”, i.e. logical disjunction. In everyday English usage, the word “or” means “either this or that”. In computer programming, “or” means “this or that or both”, i.e. inclusive or. The following table should help to clarify the meaning of || and its effect when given two boolean values. The table below uses OR instead of || because Markdown cannot properly convert || when used within a table.

ORFalseTrue
FalseFalseTrue
TrueTrueTrue

From the above table, the result of False || True is True, so is True || True. The one and only occasion when || returns False is when both operands are False. Take a moment to use the above table and work through the output of the following program.

or.hs

1
2
3
4
5
6
7
8
9
10
11
import Text.Printf

-- | Boolean or.
main = do
    let coffee = True
    let tea = False
    let water = True
    let beer = False
    printf "Coffee or tea? %s\n" $ show (coffee || tea)
    printf "Tea or water? %s\n" $ show (tea || water)
    printf "Tea or beer? %s\n" $ show (tea || beer)

Dollar sign

Hang on. What is the dollar sign $ doing in the program or.hs ? In Haskell, the symbol $ is the function application operator. Given a function f and an argument x, the result of applying f on x is written in Haskell as f x. Using the operator $ in prefix notation, we can write ($) f x for the same effect. See for yourself.

1
2
3
4
5
6
7
8
9
10
11
12
ghci> negate 5
-5
ghci> ($) negate 5
-5
ghci> head "8-)"
'8'
ghci> ($) head "8-)"
'8'
ghci> div 9 2
4
ghci> ($) div 9 2
4

At first glance, it seems like the operator $ is redundant. Why should it even exist? A short answer is that $ allows us to write readable code by avoiding (nested) parentheses as much as possible. Let’s consider an example. To satisfy your daily mathematics fix, you evaluate the expression $2 \times (4 + 3)$ to 14. You know that anything within parentheses must be calculated first, then multiply the result by 2. The symbol $ allows you to tell Haskell to evaluate the expression within parentheses first. Here are equivalent ways to evaluate $2 \times (4 + 3)$ in Haskell.

1
2
3
4
ghci> 2 * (4 + 3)
14
ghci> (*) 2 $ 4 + 3
14

The operator $ means that Haskell should treat the right-hand side of $ as an operand of *. In general, the function application operator $ directs Haskell to first evaluate or process whatever expression is to the right of $. Whenever you have an expression that involves $, you should try in your mind to parse the expression from right to left instead of the usual left to right. The examples below should clarify how to use $.

1
2
3
4
5
6
7
8
9
10
11
12
13
ghci> import Text.Printf
ghci> tail (init "8caterpillar-")
"caterpillar"
ghci> tail $ init "8caterpillar-"
"caterpillar"
ghci> printf "Quotient is %d\n" (div 11 3)
Quotient is 3
ghci> printf "Quotient is %d\n" $ div 11 3
Quotient is 3
ghci> head (tail (tail "abcdef"))
'c'
ghci> head $ tail $ tail "abcdef"
'c'

And

“And you do Addition?” the White Queen asked. “What’s one and one and one and one and one and one and one and one and one and one?”
“I don’t know,” said Alice. “I lost count.”
“She can’t do Addition,” the Red Queen interrupted.
— Lewis Carroll. Through the Looking-Glass. Macmillan, 1871, Chapter IX.

The boolean operator && means “and”, i.e. logical conjunction. Its result is True provided that both operands are True. Its result is False for all other cases. The table below helps to clarify the effect of &&.

&&FalseTrue
FalseFalseFalse
TrueFalseTrue

Unlike the expression True || False, the result of True && False is False. The only time when && returns True is the expression True && True. Again, take some time to work through the boolean results of the following program.

and.hs

1
2
3
4
5
6
7
8
9
10
11
import Text.Printf

-- | Boolean and.
main = do
    let coffee = True
    let tea = False
    let water = True
    let beer = False
    printf "Coffee and tea? %s\n" (show (coffee && tea))
    printf "Coffee and water? %s\n" (show (coffee && water))
    printf "Tea and beer? %s\n" (show (tea && beer))

Exercises

Exercise 1. Simplify the statement: “Haskell is not not fun.”

Exercise 2. What’s the back of your back?

Exercise 3. Rewrite the program or.hs by using $ to replace the outermost pairs of parentheses. Rewrite the program and.hs to use as few parentheses as possible.

Exercise 4. Examine the terminal session below. Determine which food Tabby dislikes. Write a program that uses boolean operators to achieve the same output as in the terminal session.

1
2
3
4
5
$ ghc food.hs && ./food
[1 of 2] Compiling Main             ( food.hs, food.o )
[2 of 2] Linking food
Tabby likes fish or cheese? True
Tabby likes fish and cheese? False

Exercise 5. Both of the types Bool and Int are based on Enum, as can be verified by the output of the GHCi commands :info Bool and :info Int. The method fromEnum can be used to convert a boolean value to its corresponding integer value: True becomes 1, False becomes 0. A quiz has four questions: a, b, c, and d. Your results for the quiz are given below. The value True means you answered a question correctly and False means otherwise. Use the boolean values and the method fromEnum to calculate how many questions you answered correctly.

1
2
3
4
ghci> a = True
ghci> b = False
ghci> c = True
ghci> d = True

Exercise 6. The word “or” in everyday English means, “Either this or that, but not both.” In computer programming, the latter meaning of “or” is called exclusive or, often abbreviated as XOR. Given two boolean values a and b, the boolean operator XOR is defined in terms of || and && as the expression

1
(a || b) && not (a && b)

Fortunately, you do not need to use the above expression whenever you want to calculate the XOR of two boolean values. The package Data.Bits has the method xor. Write a program that uses xor to achieve the same output as shown in the terminal session below.

1
2
3
4
5
6
$ ghc pet.hs && ./pet
[1 of 2] Compiling Main             ( pet.hs, pet.o )
[2 of 2] Linking pet
Sam likes cats and dogs? False
Sam likes cats or dogs? True
Sam likes cats XOR dogs? True

Exercise 7. Modify the following program so the expression likeCat && likeTiger returns False.

pet.hs

1
2
3
4
5
6
7
8
9
10
11
12
import Text.Printf

-- | Pet preference.
main = do
    let likeCat = True
    let likeDog = True
    let likeTiger = True
    let likeWolf = False
    printf "Sam likes cats and dogs? %s\n" $ show $ likeCat && likeDog
    printf "Sam likes tiger or wolf? %s\n" $ show $ likeTiger || likeWolf
    printf "Sam likes cat or tiger? %s\n" $ show $ likeCat || likeTiger
    printf "Sam likes cat and tiger? %s\n" $ show $ likeCat && likeTiger

Exercise 8. Given two boolean values a and b, De Morgan’s laws are the statements:

1
2
not (a || b) == (not a) && (not b)
not (a && b) == (not a) || (not b)

Verify the above statements yourself for various boolean values of a and b.

Exercise 9. Sam is using a search engine to find pet images. The search query is, “cat or dog”. Provide an equivalent boolean expression for Sam’s query.

Exercise 10. Determine the output of the following.

1
2
3
ghci> import Text.Printf
ghci> likeCat = True
ghci> putStrLn $ printf "%s" $ show $ not $ not likeCat
  1. Ackchyually, || and && are boolean functions.