📘
HPM Education - Haskell
  • Introduction to Haskell
  • Introduction
    • Functions
    • Functional Programming vs Imperative Programming
    • Installing Haskell
    • Haskell Modules
    • Loading Modules into GHCi
    • Expressions
    • Laziness
    • Immutability
  • Types in Haskell
    • Introduction
    • Basic Types
    • Static Type Check
    • Polymorphic and Overloaded Types
    • Data Structure Types
      • Lists
        • List Functions
      • Tuples
    • Function Types
      • Curried Functions
      • Partial Application
  • Defining Functions / Working with Functions
    • The Layout Rule
    • Local Definitions
    • The Infix Operator
    • Conditionals
      • If-then-else Statements
      • MultiWayIf
      • Guarded Equations
      • Case-of Statements
    • Pattern Matching
      • Tuple Patterns
      • List Patterns
    • Lambda functions
    • Function Operators
  • List Comprehensions
    • List Comprehensions
  • Higher-order Functions
    • Introduction
    • The map Function
    • The filter Function
  • Recursion
    • Introduction
    • 4 Steps to Defining Recursive Functions
    • Recursion Practice
    • Folds
      • Fold Right (foldr)
      • Fold Left (foldl)
  • Cutom Types
    • Declaring Types
      • Type Synonyms
      • Data Declarations
      • Newtype declarations
  • Type Classes
    • Introduction
    • Basic Classes
      • Eq – Equality Types
      • Ord – ordered types
      • Show – Showable Types
      • Read – readable types
      • Num – Numeric Types
      • Integral – Integral Types
      • Fractional – Fractional Types
      • Enum – Enumeration Types
    • Derived Instances
    • Exercise – Making a Card Deck Type
  • Interactive Programming
    • Introduction
    • Input / Output Actions
    • Sequencing Actions
    • Exercise - Numbers Guessing Game
  • Functors, Applicatives and Monads
    • Introduction
    • Functors
    • Applicative Functors
    • Monads
      • Maybe Monad
      • List Monad
      • Monad Laws
  • References / Further Reading
Powered by GitBook
On this page

Was this helpful?

  1. Defining Functions / Working with Functions
  2. Conditionals

MultiWayIf

MultiWayIf allows us to create multiple cases for our if statements without nesting them. We define a sequence of expressions that evaluate to either True or False (conditions called guards) and associate an expression with each of them:

if | <CONDITION1> -> <EXPRESSION1>
   | <CONDITION2> -> <EXPRESSION2>
   | ...
   | <CONDITIONx> -> <EXPRESSIONx>
   | otherwise    -> <EXPRESSION>

The symbol | can be read as "such that..." or "where...". The guards are evaluated from top to bottom, and the expression associated with the first guard that is True is chosen for further evaluation. The otherwise function simply always evaluates to True and the expression associated with it will always be further evaluated if none of the guards before it evaluates to True. It gives us a convenient way to make sure that we have handled all possible cases. It is not necessary to add the otherwise guard at the end, but if all the possible cases are not met, we will end up with an error at runtime.

One more thing we have to do in order to use the MultiWayIf is to enable the extension in GHC. GHC has a number of special features that are disabled by default (like the MultiWayIf) so we have to add a line above our module declaration in the following format to enable it:

{-# LANGUAGE MultiWayIf #-} 
module Practice where
...

Let's switch our trackScore function to use MultiWayIf instead, but not include the case where time == avg. It will result in an error at runtime (not during compilation) because the case we entered has not been defined:

trackScore :: Float -> Float -> String
trackScore time avgTime = 
  if | time < avgTime -> "Great! Your time is " ++ show (avgTime - time) 
         ++ " seconds below average!"
     | time > avgTime -> "Your time is " ++ show (time - avgTime)
         ++ " seconds above average."

ghci> :r
[1 of 1] Compiling Practice ( practice.hs, interpreted )

*Practice> trackScore 10 10
"*** Exception: practice.hs:(74,1)-(76,89): Non-exhaustive patterns in 
function trackScore"

Let's fix it up:

trackScore :: Float -> Float -> String
trackScore time avgTime = 
  if | time < avgTime -> "Great! Your time is " ++ show (avgTime - time) 
         ++ " seconds below average!"
     | time > avgTime -> "Your time is " ++ show (time - avgTime) ++ " 
         seconds above average."
     | otherwise -> "Your time is on par with the average time!"

This already reads much better than our first implementation with nested if-statements, but there is a way to make it even nicer by using guarded equations.

PreviousIf-then-else StatementsNextGuarded Equations

Last updated 2 years ago

Was this helpful?