Exclusive Or

An exclusive or is when you want to test for one thing or another but not both. For example, for a word to be a valid plural, it should have an s on the end (like cats) or it should be a collective noun (like sheep) but not both (like sheeps). The normal or test uses the || operator which tests for one thing or another or both. The idea here is to provide a function which can be used instead of || to do exclusive or testing. This name of the function is conventionally xor, which is not very good, but it is difficult to do better.

module Xor where

-- Exclusive or. 
xor :: a         
xor = undefined

To decide on the type, let's find the type of || and copy it:

> ghci
Prelude> :type ||

That doesn't work. If we look in the reference pages, we can find the type of ||. At the same time, we might notice that when an operator is mentioned on its own, rather than with arguments on either side, it has brackets round it, i.e. "(||)" means "the operator ||". We can try this with ghci:

> ghci
Prelude> :type (||)
(||) :: Bool -> Bool -> Bool

That gives us our answer. Let's try writing down the definition of our function straight away with pattern matching (improving the comment at the same time):

module Xor where

-- Find the exclusive or of two booleans.
xor :: Bool -> Bool -> Bool
xor false false = false
xor false true = true
xor true false = true
xor true true = false

Unfortunately, this doesn't work:

> ghci Xor.hs
Xor.hs:5:
    Conflicting definitions for `false'
    In the definition of `xor'
...

The error message tells us that false is being treated as an argument variable, when we expected it to be treated as a constant. If we look it up in the reference pages, it has a capital letter, i.e. it is spelled False. Let's try that change:

module Xor where

-- Find the exclusive or of two booleans.
xor :: Bool -> Bool -> Bool
xor False False = False                   
xor False True = True                     
xor True False = True                     
xor True True = False

A little testing shows that this is now OK.

> ghci Xor.hs
*Xor> xor True False
True

This illustrates something about pattern matching generally. Suppose we were to define our own version of the standard not function, called bnot to avoid a name clash:

bnot :: Bool -> Bool
bnot false = True
bnot true = False

GHC gives a warning but doesn't prevent compilation. But this function would always give the answer True. This is because the name false on the left hand side of the first statement is treated as an argument variable name, exactly as if we had written:

bnot :: Bool -> Bool
bnot b = True
bnot b = False

This means that the first statement is always used, no matter what the value of the argument, and always gives the answer True. The same happens even if we define variables called true to act as synonyms for the constants:

bnot :: Bool -> Bool
bnot false = True
bnot true = False

false :: Bool
false = False

true :: Bool
true = True

This is because local variables are always completely separate from global ones, so the name false on the left hand side of the first statement is a completely separate variable from the global one we have just defined. The global variable false can be used interchangeably with the constant False everywhere except when pattern matching on the left hand side; (false is not a constant; it is a variable which happens to have a constant value).

The rule with pattern matching is that anything starting with a lower case letter is a variable. To get matching to happen, you have to use constants or constructors which have upper case initial letters or other special notations. The other notations include constructor operators starting with the : character, numerical constants, tuples, and explicit lists with square brackets. In fact, you can use anything referred to in the reference pages as a constant or constructor.