Haskell has a convenient notation for writing numeric ranges. It also works with a lot of types other than numbers, including types that you define yourself.
This example uses type applications.
{-# LANGUAGE TypeApplications #-}
Int8
is the type for signed eight-bit integers. We will use this as an example of a bounded type.
import Data.Int (Int8)
The data
keyword introduces the definition of a new datatype. The Rank
type represents the ranks in a deck of playing cards, as you might need to do in writing a poker or solitaire program. The values are listed here in an order that represents their hierarchy in poker.
The deriving
clause tells the compiler to create three things for this type:
Bounded
creates an upper and lower boundary for the type, based on the order in which the values are listed (two is the lowest rank, and ace is the highest);Enum
helps the compiler understand this type as a series of values, with predecessors and successors, allowing us to use functions for creating ranges or enumerated lists; andShow
provides a conversion between this type’s values and human-readable strings we can print to the terminal.
data Rank =
Rank2
| Rank3
| Rank4
| Rank5
| Rank6
| Rank7
| Rank8
| Rank9
| Rank10
| Jack
| Queen
| King
| Ace
deriving (Bounded, Enum, Show)
In these first two examples, we use range syntax to enumerate lists of numbers and characters. Range syntax is available only for datatypes that have Enum
instances. We give the starting point and the ending point of the range we want to construct, and the two dots in between signify enumerate from that start to that finish (inclusive).
=
main do
putStrLn (show [3 .. 8])
putStrLn (show ['a' .. 'z'])
Since we gave our Rank
type an instance of Enum
, we can use range syntax to create lists of playing card ranks.
putStrLn (show [Rank2 .. Rank10])
You do not have to give an ending point for the enumeration; here we print a list from Jack
to the highest rank.
putStrLn (show [Jack ..])
minBound
and maxBound
give the lowest and highest values of a type with a Bounded
instance.
These expressions are polymorphic; they have a type parameter. We can use type application (with the @
keyword) to specify which type we want. Usually, the compiler figures out how to fill type parameters automatically. Explicit type application is only needed in situations where the the type would otherwise be ambiguous.
putStrLn (show (minBound @Rank))
putStrLn (show (maxBound @Rank))
If we change the type argument, the same expression produces a different result.
putStrLn (show (minBound @Int8))
putStrLn (show (maxBound @Int8))
To list all values of the type, use the boundaries determined by deriving Bounded
to construct a range from the lower bound to the upper bound.
putStrLn (show [minBound @Rank .. maxBound @Rank])
$ runhaskell enum-ranges.hs [3,4,5,6,7,8]
When printing a list of characters, the result appears as a string ("abc..."
) rather than as a list (['a', b', 'c', ...]
).
"abcdefghijklmnopqrstuvwxyz"
[Rank2,Rank3,Rank4,Rank5,Rank6,Rank7,Rank8,Rank9,Rank10]
[Jack,Queen,King,Ace]
Rank2
Ace
-128
127
[Rank2,Rank3,Rank4,Rank5,Rank6,Rank7,Rank8,Rank9,Rank10, Jack,Queen,King,Ace]
Next: For loops