Learn You a Haskell for Great Good! - Miran Lipovaca [56]
ghci> Nothing < Just 100
True
ghci> Nothing > Just (-49999)
False
ghci> Just 3 `compare` Just 2
GT
ghci> Just 100 > Just 50
True
However, we can’t do something like Just (*3) > Just (*2), because (*3) and (*2) are functions, which are not instances of Ord.
Any Day of the Week
We can easily use algebraic data types to make enumerations, and the Enum and Bounded type classes help us with that. Consider the following data type:
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
Because all the type’s value constructors are nullary (that is, they don’t have any fields), we can make it part of the Enum type class. The Enum type class is for things that have predecessors and successors. We can also make it part of the Bounded type class, which is for things that have a lowest possible value and highest possible value. And while we’re at it, let’s also make it an instance of all the other derivable type classes.
data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
deriving (Eq, Ord, Show, Read, Bounded, Enum)
Now let’s see what we can do with our new Day type. Because it’s part of the Show and Read type classes, we can convert values of this type to and from strings.
ghci> Wednesday
Wednesday
ghci> show Wednesday
"Wednesday"
ghci> read "Saturday" :: Day
Saturday
Because it’s part of the Eq and Ord type classes, we can compare or equate days.
ghci> Saturday == Sunday
False
ghci> Saturday == Saturday
True
ghci> Saturday > Friday
True
ghci> Monday `compare` Wednesday
LT
It’s also part of Bounded, so we can get the lowest and highest day.
ghci> minBound :: Day
Monday
ghci> maxBound :: Day
Sunday
As it’s an instance of Enum, we can get predecessors and successors of days and make list ranges from them!
ghci> succ Monday
Tuesday
ghci> pred Saturday
Friday
ghci> [Thursday .. Sunday]
[Thursday,Friday,Saturday,Sunday]
ghci> [minBound .. maxBound] :: [Day]
[Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday]
Type Synonyms
As mentioned earlier, when writing types, the [Char] and String types are equivalent and interchangeable. That’s implemented with type synonyms.
Type synonyms don’t really do anything per se—they’re just about giving some types different names so that they make more sense to someone reading our code and documentation. Here’s how the standard library defines String as a synonym for [Char]:
type String = [Char]
The type keyword here might be misleading, because a new type is not being created (that’s done with the data keyword). Rather, this defines a synonym for an existing type.
If we make a function that converts a string to uppercase and call it toUpperString, we can give it a type declaration of this:
toUpperString :: [Char] -> [Char]
Alternatively, we can use this type declaration:
toUpperString :: String -> String.
The two are essentially the same, but the latter is nicer to read.
Making Our Phonebook Prettier
When we were dealing with the Data.Map module, we first represented a phonebook with an association list (a list of key/value pairs) before converting it into a map. Here’s that version:
phoneBook :: [(String, String)]
phoneBook =
[("betty", "555-2938")
,("bonnie", "452-2928")
,("patsy", "493-2928")
,("lucille", "205-2928")
,("wendy", "939-8282")
,("penny", "853-2492")
]
The type of phoneBook is [(String, String)]. That tells us that it’s an association list that maps from strings to strings, but not much else. Let’s make a type synonym to convey some more information in the type declaration.
type PhoneBook = [(String,String)]
Now the type declaration for our phonebook can be phoneBook :: PhoneBook. Let’s make a type synonym for String as well.
type PhoneNumber = String
type Name = String
type PhoneBook = [(Name, PhoneNumber)]
Haskell programmers give type synonyms