[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
| NothingAnother 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)
| Emptynewtypes 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 failfib 0 = 1
fib 1 = 1
fib x = fib (x - 1) + fib (x - 2)max x y | x > y = x
| otherwise = yisEmpty :: forall a. [a] -> Boolean
isEmpty [] = true
isEmpty [x|xs] = falseshowPerson :: { 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 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}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