We want to mathematically model types as sets (naively speaking). A type is just a "bag" of values.
## How to bake ADTs.
Additive ADTS,
data Bool = True | False
data Direction = Right | Left
data Spin = Up | Down
data Colour = Red | Blue | Green
Cardinality of types, ie, number of different values a type can have. Eg, Bool has cardinality 2, colour has cardinality 3, etc.
Multiplicative types
data User = User String String
More genrally
data Result = Error String | Success User
We can generalise this in various ways:
- parametric polymorphism and ADTs, ie, `
data Result a e = Error e | Success a
data Maybe a = Nothing | Just a
- they can be recursively defined
data JsValue = JsNull | JsString String | JsNumber Double | JsBool Bool | JsArray [JsValue] | JsObject (Map String JsValue)
data List a = Empty | NonEmpty a (List a)
identity :: Integer -> Integer
identity x = 0 -- wrong!!
identity x = x +1 -- wrong too!!
identity :: a -> a
identity x = x
Remeber the exercise with dates for another example of how polymorphism can help focusing on BL rather than implementation details. And you can capture your BL requirements into type constrains.
We will expand on this later
Option -1:
head :: [Int] -> Int
head [] = -1 -- very bad idea , using values to describe error / invalid scenarios , because there are indistinguishable from happy path cases
head (h : _) = h
If you turn on polymorphism you can eliminate option -1
Option 0 : exceptions, ie, enriching the output set, but in a type unsafe way (exceptions are runtime behaviour)
head :: [a] -> a
head [] = ??? -- throwing ?!
head (h : _) = h
Option 1:
realising that the signature is not appropriate, it's a lie, i can't pretend to always return a value of type a
for every [a]
head :: [a] -> Maybe a
head [] = Nothing
head (h : _) = Just h
This is type safe.
Option 2:
data NonEmptyList a = NonEmptyList a [a]
head :: NonEmptyList a -> a
head (NonEmptyList h _) = h
f :: a -> b
map:: (a -> b) -> ([a] -> [b])
We focus on the building blocks (ie, a function from a
to b
, this is the part specific of the domain) and then you can rely on functions like map
(and other ones) to "promote" that function to automatically be also a function between [a] -> [b]
, Maybe a -> Maybe b
, Set a -> Set b
, etc