[ToC]
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
newtype
s 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
fib 0 = 1
fib 1 = 1
fib x = fib (x - 1) + fib (x - 2)
max x y | x > y = x
| otherwise = y
isEmpty :: forall a. [a] -> Boolean
isEmpty [] = true
isEmpty [x|xs] = false
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"
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}
We can also pattern match on Map
s, and this is very similar to Record
s, 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}
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.
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