23 Nov

Maybe

Denotes an optional result

data Maybe a = Nothing | Just a

For an operation that can fail

divide :: [Int] -> Int -> Maybe Int
divide n 0 = Nothing  -- can't divide by zero, fail
divide n m = Just (n `div` m)  -- successful division

Or as an optional argument

power :: Maybe Int -> Int -> Int
power (Just b) e = b^e  -- extract the base from the optional datatype
power Nothing e  = 2^e  -- use base 2 if none specified

Either

Mutual exclusion of two elements

data Either a b = Left a | Right b

Possibly of two different types (Int and String)

test_results :: [Either Int String]  -- either got a grade, or something happened
test_results = [Left 5, Left 10, Right "absent", Left 7, Right "cheated"]

Using them

anyone_absent :: [Either Int String] -> Bool
anyone_absent (Right s : xs) = (s == "absent") || anyone_absent xs  -- check what happened
anyone_absent (Left i : xs) = anyone_absent xs  -- just skip grades

Exporting

Enables abstraction through modules.

In one file we define a secret encryption, exporting only encrypt and key_size:

module SecretModule (encrypt, key_size) where
key = 4321  -- not exported
key_size = 4
encrypt x = x `mod` key

In another file, or in ghci, we use the defined variables

import SecretModule   -- import everything this module exports

> key_size
4

> encrypt 5578
1257

> key  -- not exported
<error> "Variable not in scope"

Import only some variables

import SecretModule (encrypt)  -- import ONLY the encryption function
import SecretModule hiding (key_size) -- import everything EXCEPT the key size; useful in avoiding name clashes

Polymorphism

The + operator works on both Int and Double objects, returning the appropriate type.

> 1 + 2
3  -- an Int

> 1.5 + 0.5
2.0  -- a Double

From its signature we can tell that it takes two objects of any type, a, and returns one of the same kind. It has the added constraint that this type must be numeric, Num a =>.

> :t (+)  -- consult the signature
(+) :: Num a => a -> a -> a

Other examples include the equality operator =, the inclusion function, elem, etc.

Writing your own polymorphic functions requires no additional setup — just don’t write a specific data type in the function signature. Convention dictates that their type should be named a (short for anything).