Skip to content

Instantly share code, notes, and snippets.

@runjak
Created February 28, 2017 21:56
Show Gist options
  • Save runjak/87fa207b9f1e0be906ed18e209f3cecf to your computer and use it in GitHub Desktop.
Save runjak/87fa207b9f1e0be906ed18e209f3cecf to your computer and use it in GitHub Desktop.
Example Haskell code on using pattern matching. Mostly comments. Also a Tree type is defined and stuff is done with it.
module Tree where
{-
This is code for a Haskell module named `Tree`.
You're currently reading a comment block in it.
Below this comment we define a data type called `Tree`.
It has two constructors, named `Leaf` and `Branch`.
The `Leaf` constructor takes one argument, which must be of type `Int`.
Given such an `Int` it produces a value of type `Tree` for us.
The `Branch` constructor takes two arguments, both of which must be of type `Tree`.
Just like `Leaf` it produces a value of type `Tree`.
The `deriving (Show)` part makes sure that Haskell
can automagically print a `Tree` as a `String.`
-}
data Tree = Leaf Int
| Branch Tree Tree
deriving (Show)
{-
We can have some example trees and call them silly names like `a`, `b`, `c`.
We can specify that the type of `a` shall be `Tree` by saying `a :: Tree`.
But as is visible with `b`,`c` we can also ommit the type assertions
in cases where Haskell can infer them and we feel confident enough that the
type isn't going to be too complicated.
-}
a :: Tree
a = Leaf 5
b = Branch (Leaf 2) (Leaf 3)
c = Branch a b
{-
We can inspect our code so far by firing up a ghci session with `ghci Tree.hs`.
Issuing the command `:i Tree` allows us to inspect a type
and figure out what ghci 'thinks' of it.
This may often be helpful to discover how bits of code fit together.
We can use commands like `:t Leaf` or `:t Branch` to inquire about the types of the constructor functions.
For example ghci may tell us that `Leaf :: Int -> Tree`,
which signifies that `Leaf` is a function that will produce a value of type `Tree`
given a value of type `Int`.
Lastly we can enter value expressions in ghci directly.
Examples of this are expressions like `1+2+3`
or our previously defined trees `a`,`b` and `c`.
-}
{-
Here we define the function `findMaximum`,
which takes a `Tree` as its parameter and produces an `Int` as a result.
The lines that start with `findMaximum (Leaf x)` and `findMaximum (Branch b1 b2)`
define two different cases of the function.
In these lines the parts `Leaf` and `Branch` are not used in their function
as constructors of values of type `Tree`,
but are used to deconstruct a value of type `Tree` into it's parts.
This is called pattern matching because we're matching on the different patterns of constructors.
Given that we indeed have a `Tree` that only consists of a single `(Leaf x)`,
as the first pattern suggests, the result of `findMaximum` will just be that value of `x`.
Given that the `Tree` instead consists of a `(Branch b1 b2)`,
where `b1` and `b2` stand for arbitrary values of type `Tree`,
the rule for the second pattern will be used.
Trough recursively calling itself `findMaximum` is able to find the largest
`Int` value stored on a `Leaf` of a given `Tree`.
Using a ghci session it is possible to try this out using commands like
`findMaximum a`, where `a` is our previously defined example `Tree`,
or `findMaximum (Branch (Branch (Leaf 0) (Leaf 1)) (Leaf 7))`.
-}
findMaximum :: Tree -> Int
findMaximum (Leaf x) = x
findMaximum (Branch b1 b2) = let x1 = findMaximum b1
x2 = findMaximum b2
in if x1 >= x2 then x1 else x2
{-
Here are some more examples of pattern matching:
The function `spellNumber` performs pattern matching on an `Int`.
In this case we can act as if `0` was a constructor for values of type `Int`,
and pattern match on specific `Int` values.
When pattern matching `_` is used as a wildcard and catches all other cases.
The function `thirdFromTriple` works similar to `spellNumber`,
but allows us to extract the third field from a triple.
An example command for this would be `thirdFromTriple (2, 3, 5)`.
The function `secondItem` consumes a list of `Int`,
and tries to return the second item from that.
However there are of course lists that are only one element long or completely empty.
Because of this the compiler would usually warn us if we tried to compile
this code, but in ghci we can still explore it and watch it throwing an exception for shorter lists.
Try these commands:
`secondItem [6,7,8]`
`secondItem []`
`secondItem [6]`
-}
spellNumber :: Int -> String
spellNumber 0 = "zero"
spellNumber 1 = "one"
spellNumber 2 = "two"
spellNumber _ = "Cannot spell that number."
thirdFromTriple :: (Int, Int, Int) -> Int
thirdFromTriple (_, _, x) = x
secondItem :: [Int] -> Int
secondItem (_:x:_) = x
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment