Case expressions are useful for conditionals that have two or more branches. Some other languages call this kind of expression a “switch”. As mentioned on the previous page, pattern guards can also be used to a similar effect.
This program uses the time
package for handling time conversions.
import Data.Time
The branches are introduced once by case...of
. Haskell doesn’t typically use curly braces, but the of
is necessary and the indentation matters.
The values on the left side of the ->
arrows are the cases of the type that the expression in case <exp> of
evaluates to. The <
function returns a Boolean, so this an alternate to if
expressions but with explicit matching.
=
timeNow now case todHour (localTimeOfDay (zonedTimeToLocalTime now)) < 12 of
True -> putStrLn "It's before noon"
False -> putStrLn "It's after noon"
case
expressions can be used to match on the values of a custom datatype. Here is a custom type representing three mutually exclusive service plans.
The billAmount
function associates a ServicePlan
value to a numeric value.
data ServicePlan = Free | Monthly | Annual
=
billAmount plan case plan of
Free -> 0
Monthly -> 5
Annual -> billAmount Monthly * 12
Some types have more values than you want to match on manually, and maybe some of the possible inputs are irrelevant to your needs. An underscore in the final case is a catchall that will match against any value.
=
writeNumber i case i of
1 -> "one"
2 -> "two"
3 -> "three"
-> "unknown number" _
You can use those functions in a main
program by declaring some variables and applying the functions to them within the do
-block. This is what runhaskell
will run (see below).
=
main do
<- getZonedTime
now
timeNow now
let plan = Free
putStrLn ("Customer owes " ++ show (billAmount plan)
++ " dollars.")
let i = 2
putStrLn ("Write " ++ show i ++ " as " ++ (writeNumber i))
Let’s use the REPL to check our work. Passing the filename you want to load into the GHCi session ensures that all the things you need are in scope at the start of your session.
$ ghci branching.hs
GHCi can give lots of information about programs. This session demonstrates applying single functions to appropriate arguments.
λ> billAmount Monthly
5
λ> billAmount Annual
60
λ> writeNumber 6
"unknown number"
If you try applying billAmount
to a number, or writeNumber
to a Boolean, GHCi will become annoyed. Displayed here are the first lines of the error messages you’d receive for these examples. Both of them are conveying the information that neither ServicePlan
values nor Bool
values are numbers, so they cannot be used interchangeably with numbers.
λ> billAmount 5
<interactive>:3:12: error:
• Could not deduce (Num ServicePlan)
arising from the literal ‘5’
λ> writeNumber True
<interactive>:4:1: error:
• No instance for (Num Bool)
arising from a use of ‘writeNumber’
The >>=
function can be thought of as a means of passing the output of the first function, getZonedTime
, to a second function. The first example only prints the output of getZonedTime
. The second example passes the output of getZonedTime
to our timeNow
function.
λ> getZonedTime >>= print
2019-07-30 14:04:19.224690712 MDT
λ> getZonedTime >>= timeNow
It's after noon
When you want to quit GHCi, use :quit
.
λ> :quit
Leaving GHCi.
You can use runhaskell
to run the main
program. Note that outputs that depend on the current time may be different when you run the program locally.
$ runhaskell branching.hs
It's after noon
Customer owes 0 dollars. Write 2 as two
Next: Using the REPL