Tuples
pos3, neg3 :: (Int, Bool)
pos3 = (3, True)
neg3 = (3, False)
Functions
plus3 :: Int -> Int
plus3 x = 3 + x
data Trace = Trace {var :: Char, tag :: Int}
newtrace :: Trace
newtrace = Trace 't' 1
trace2 :: Trace
trace2 = Trace {var = 't', tag = 4}
data NP = Trace | Name String
john :: NP
john = Name "John"
x :: NP
x = trace2
Name convert values from one type to another, in
this case strings to NPs. They are functions that take values and return
values:t Name
Name :: String -> NP
data List a = Empty | Cons a (List a)
domain :: List Entity
domain = Cons John (Cons Mary Nil)
-- domain :: [Entity]
-- domain = [John, Mary]
data Pair a = Pair (a, a)
theSmiths :: Pair Entity
theSmiths = Pair (John, Mary)
point :: Pair Int
point = Pair (3, 4)
show function, which we define for each displayable type, including our
ad-hoc NP type.instance Show NP where
show (Trace v n) = "Var: " + show v + show n
show (Name s) = "Name: " + s
instance Eq a => Eq (List a) where
Nil == Nil = True
(Cons x xs) == (Cons y ys) = x == y && xs == ys
_ == _ = False
The show and (==) functions are overloaded to work on many different
types, and what they do with each type is your prerogative.
This makes sense for show, but you might expect (==) to be somewhat more
restricted. For instance, this definition doesn't match anybody's
expectations about what an equality test should do.
instance Eq NP where
_ == _ = False
For things like equality, we say that any specification of the (==)
relation for some particular type should satisfy a few well-known conditions.
class Functor m where
fmap :: (a -> b) -> m a -> m b
-- note that a and b can be any types whatsoever here; this tells you that
-- being "mappable" is a property of the type constructor, regardless of what
-- sorts of values it contains
instance Functor (List a) where
fmap _ Nil = Nil
fmap f (Cons x xs) = Cons (f x) (fmap f xs)
instance Functor (Pair a) where
fmap (Pair (x, y)) = Pair (f x, f y)
Any specialization of the mapping function fmap for a custom type M
should satisfy:
fmap id m is equivalent to m, for any m :: Mfmap (g ○ h) m is equivalent to fmap g (fmap h m) , for any m :: MWhen it does, we call M a functor under its definition of fmap
Another commonly overloaded function is a close cousin of fmap called
bind, spelled (>>=).
Where different definitions of fmap specify how to map a simple a -> b
function over some structure full of as (to end up with a structure full of
bs), definitions of bind specify how to map an structure-generating
function of type a -> m b over a structure full of as (to end up with a
structure full of bs.
class Functor m where
fmap :: (a -> b) -> m a -> m b
class Bind m where
bind :: (a -> m b) -> m a -> m b
instance Bind (List a) where
bind f Nil = Nil
bind f (Cons x xs) = concat $ Cons (f x) (bind f xs)
instance Bind (Pair a) where
bind f (Pair (x,y)) = Pair (fst (f x), snd (f y))
These structure-generating a -> m b functions are called Kliesli
arrows.
Any type for which there is also a special "trivial" Kliesli arrow is called a monad.
class Bind m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>=) = flip bind
instance Monad (List a) where
return x = Cons x Nil
instance Monad (Pair a) where
return x = Pair (x, x)
But as with (==) and fmap, these definitions of bind and return are
expected to satisfy certain laws
return x >>= f is equivalent to f xm >>= return is equivalent to m(m >>= f) >>= g is equivalent to m >>= (\x -> f x >>= g)Loosely speaking, the left identity law guarantees that definitions of
return don't have any side effects.
The right identity law guarantees that definitions of return don't consume
or modify any side effects.
The associativity law says that chains of effects should be linear
And that's it. A monad is a singly-parameterized type constructor together with a mapping function called bind and an injection function called unit that satisfy the monad laws.