Site Map - skip to main content

Hacker Public Radio

Your ideas, projects, opinions - podcasted.

New episodes every weekday Monday through Friday.
This page was generated by The HPR Robot at


hpr2888 :: Pattern matching in Haskell

Tuula talks about one of their favourite features in Haskell

<< First, < Previous, , Latest >>

Thumbnail of Tuula
Hosted by Tuula on 2019-08-28 is flagged as Clean and is released under a CC-BY-SA license.
pattern matching. (Be the first).
The show is available on the Internet Archive at: https://archive.org/details/hpr2888

Listen in ogg, spx, or mp3 format. Play now:

Duration: 00:20:36

Haskell.

A series looking into the Haskell (programming language)

Pattern matching is one of those features of Haskell that immediately got me interested as it reduces amount of branching inside of functions I write. Basic idea is that if value constructors are for making data, pattern matching is for taking it apart.

First example is a function that takes a Bool and returns a respective String:

boolToString :: Bool -> String
boolToString n =
    if n
        then "True"
        else "False"

Nothing too fancy, just an if expression inside a function. We can move that if out of there though and define exactly same functionality, but with patterns:

boolToString :: Bool -> String
boolToString True =
    "True"

boolToString False =
    "False"

There’s one definition for boolToString, but two different patterns used.

Second example is bit more complex, this time we have Maybe Int that is being turned into String. Maybe has two value constructors Nothing and Just a. We have two cases for Just, specific one for when it’s Just 1 and more general one Just n that takes care of rest of the cases.

isBig :: Maybe Int -> String
isBig Nothing =
    "Not at all"

isBig (Just 1) =
    "Just perfect"

isBig (Just n) =
    if n < 10
        then "Just slightly"
        else "Definitely is"

Some example usage:

> isBig Nothing
"Not at all"
> isBig $ Just 0
"Just perfect"
> isBig $ Just 50
"Definitely is"

Pattern matching isn’t limited to algebraic datatypes that we have been working with so far. We can do same things with records. Below is an function used to calculate total fee when cost and customer are known. Each customer can have their own discount percentage, but in addition we’re giving 10% discount to VIP customers:

data Customer = Customer
    { customerName :: String
    , customerDiscountPct :: Double
    , vipCustomer :: Bool
    }

totalFee :: Double -> Customer -> Double
totalFee bill cust@(Customer { vipCustomer = True }) =
    bill * 0.9 * customerDiscountPct cust

totalFee bill cust =
    bill * customerDiscountPct cust

There’s two cases of totalFee function. First one is for when passed in Customer has vipCustomer field True. Second one takes care of general case. In the first case we’re using @ to bind Customer as a whole to cust name.

Lists can be matched too. The basic idea is exactly the same:

  • (x:xs) matches a list with at least one item, x is first item, xs is rest of the items (might be an empty list)
  • (x:y:_) matches two first items in a list of at least two items, x is first, y is second, _ is rest
  • [] matches empty list
  • (x:[]) matches list of exactly one item

Underscore _ matches to everything without binding value to a name. This is useful when you don’t care about exact value, so you don’t want to give it a name. One could give it a name, but compiler will issue a warning if there are unused values in the code.

Next example is recursively counting amount if items in a list using pattern matching:

count :: [a] -> Int
count [] =
    0

count (x:xs) =
    1 + count xs

Fibonacci series is series of number which starts with 0, 1 and then rest of the numbers are sum of two previous ones: 0, 1, 1, 2, 3, 5, 8…

To calculate number in series, we can write following code (this is extremely slow way of calculating them by the way):

fibonacci :: Int -> Int
fibonacci 0 =
    0

fibonacci 1 =
    1

fibonacci n =
    fibonacci (n - 1) + fibonacci (n - 2)

Last trick in our sleeve for now is case expression. This allows us to do pattern matching inside of a function. Otherwise it works in the same way. Our fibonacci function could be defined as:

fibonacci :: Int -> Int
fibonacci n =
    case n of
        0 ->
            0

        1 ->
            1

        n ->
            fibonacci (n - 1) + fibonacci (n - 2)

Questions, comments and feedback are welcome. Best way to catch me nowadays is either email or in fediverse where I’m Tuula@mastodon.social


Comments

Subscribe to the comments RSS feed.

Leave Comment

Note to Verbose Commenters
If you can't fit everything you want to say in the comment below then you really should record a response show instead.

Note to Spammers
All comments are moderated. All links are checked by humans. We strip out all html. Feel free to record a show about yourself, or your industry, or any other topic we may find interesting. We also check shows for spam :).

Provide feedback
Your Name/Handle:
Title:
Comment:
Anti Spam Question: What does the letter P in HPR stand for?
Are you a spammer?
What is the HOST_ID for the host of this show?
What does HPR mean to you?