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).