Skip to content

Latest commit

 

History

History
149 lines (103 loc) · 4.02 KB

04_MoreTypesandPatternMatching.md

File metadata and controls

149 lines (103 loc) · 4.02 KB

More Types and Pattern Matching

[ToC]

Algebraic Data Types

Using algebraic data types we are saying some datatype can be one of the many things, distinguished by and identified by what is called a constructor.

For example, Maybe a is saying that If something has type Maybe a, it can either be a value which has an a type and wrapped by a constructor Just or an empty value Nothing

data Maybe a = Just a
             | Nothing

Another example is List, from its definition we can see that it has a recursive structure. So we can have whatever number of elements in our list, but they have to have the same type.

data List a = Cons a (List a)
            | Empty

Newtypes

newtypes are used to distinguish two types which have the same type of value but different units/meanings.

For example:

newtype Email = Email String

m1 :: Map Email Integer
m1 = empty
--This forces us to only pass a String with a construtor Email.
--So insert "abc" 123 m1 will fail

Simple Pattern Matching

fib 0 = 1
fib 1 = 1
fib x = fib (x - 1) + fib (x - 2)

Guards

max x y | x > y     = x
        | otherwise = y

List Patterns

isEmpty :: forall a. [a] -> Boolean
isEmpty []     = true
isEmpty [x|xs] = false

Record Patterns

showPerson :: { firstName :: Name, lastName :: Name } -> Name
showPerson { firstName: x, lastName: y } = y <> ", " <> x

> showPerson { firstName = "Phil", lastName = "Freeman" }
"Freeman, Phil"

> showPerson { firstName = "Phil", lastName = "Freeman", location = "Los Angeles" }
"Freeman, Phil"

Maps

Map is the Map from Erlang. Map k v is the type of a Map.

We can construct a Map like this:

m1 :: Map String Integer
m1 = #{"Hello" => 5, "World" => 17}

> lookup m1 "Hello"
Just  5

> insert "!" 0 m1
#{"Hello" => 5, "World" => 17, "!" => 0}

Map Patterns

We can also pattern match on Maps, and this is very similar to Records, except for some syntax changes. For example, getID let us get the ID of Wang from a map where we have to have at least Wang, Thomas and Leeming as keys.

getID :: Map String Integer -> Maybe Integer
getID #{ "Wang":= x, "Thomas" := y, "Leeming" := z } = Just x
getID _                                              = Nothing

> getID #{"Wang" => 10, "Thomas" => 20 , "Leeming" => 30 }
{'Just',10}

Binary Patterns

Matching on binaries is just like how it is done in Erlang. In the following example, we are trying to get a 24-bit integer out of the Binary passed to getA.

getA :: Binary -> Maybe (Integer, Binary, Binary)
getA << a:24/big-integer , b:4/binary-little , c:3/binary >> = Just (a,b,c)
getA _                                                       = Nothing

> getA <<0,0,1,"aaaa","bbb">>
{'Just',{1,<<"aaaa">>,<<"bbb">>}}

> getA <<0,0,1,"aaaa","bb">>
{'Nothing'}

getB :: Binary -> Maybe (Integer, Binary, Binary)
getB << a:24/big-integer , b:4/binary-little , c/binary >> = Just (a,b,c)
getB _                                                     = Nothing

> getB <<0,0,1,"aaaa">>
{'Just',{1,<<"aaaa">>,<<>>}}

> getB <<0,0,1,"aaaa","bbbbbbbbb">>
{'Just',{1,<<"aaaa">>,<<"bbbbbbbbb">>}}

Big and Little means the endianess in the part we need. Integer or Binary is the type we will give to after we extract the segment. The number of bits of the segment depends on the size of the segment we need and the type we assign. If they type we assign is an Integer then we get exact the same number of the size of bits, which is required to be evenly divisible by 8. If it is a Binary we want, it will need 8 times the size of bits.

Case Expressions

With case we can also pattern match on the value after some computations when there is no need to bind the intermediate result.

plus :: (Integer, Integer) -> Integer
plus (x, y) = x + y

sumUpTo :: Integer -> (Integer, Integer) -> Boolean
sumUpTo x p = case plus p of
                10 -> true
                _ -> false
> sumUpTo 1 (2,3)
false

> sumUpTo 1 (2,8)
true