class Eq a where
(==) :: a -> a -> Bool
(/=) :: a -> a -> Bool
x == y = not (x /= y)
x /= y = not (x == y)
class (Eq a) => Num a where
...
data TrafficLight = Red | Yellow | Green
instance Eq TrafficLight where
Red == Red = True
Green == Green = True
Yellow == Yellow = True
_ == _ = False
What if you have a type constructor instead of a type?
instance (Eq m) => Eq (Maybe m) where
Just x == Just y = x == y
Nothing == Nothing = True
_ == _ = False
Let’s practice interpreting function types
a -> b
(a -> b) -> [a] -> [b]
(b -> c) -> (a -> b) -> a -> c
class Functor f where
fmap :: (a -> b) -> f a -> f b
instance Functor [] where
fmap = map
Why is the declaration not this?
instance Functor [a] where
fmap = map
f is a type constructor
Can you write
fmap
for Maybe?
instance Functor Maybe where
fmap f (Just x) = Just (f x)
fmap f Nothing = Nothing
How about
Functor Tree
?
instance Functor Tree where
fmap f EmptyTree = EmptyTree
fmap f (Node v left right) = Node (f v) (fmap f left) (fmap f right)
instance Functor IO where
fmap f action = do
result <- action
return (f result)
What is the type constructor
(->) r
?
Well
((->) r) a
is equivalent to
(->) r a
is equivalent to
r -> a
It’s just a type constructor creating a function type
How do we implement
fmap
over function type constructors?
instance Functor ((->) r) where
fmap = ...
(a -> b) -> (r -> a) -> (r -> b)
That’s our type for function composition!
instance Functor ((->) r) where
fmap = (.)
fmap id = id
fmap (f . g) = fmap f . fmap g
fmap
?
map
fmap
lets us operate on on Functor
It turns out we can do something like this
ghci> (+) <$> Just 4 <*> Just 5
Just 9
ghci> (+) <$> Nothing <*> Just 5
Nothing
But we need some machinery first
pure
“wraps” the argument in the functor
<*>
is like fmap but the mapping function is inside the functor
class (Functor f) => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
instance Applicative Maybe where
pure = Just
Nothing <*> _ = Nothing
(Just f) <*> something = fmap f something
ghci> Just (+3) <*> Just 9
Just 12
ghci> pure (+3) <*> Nothing
Nothing
ghci> Nothing <*> Just 5
Nothing
ghci> pure (+) <*> Just 5 <*> Just 7
Just 12
pure f <*> arg
doing?
fmap f arg
<$>
is a sugary infix fmap operator
instance Applicative [] where
pure x = [x]
fs <*> xs = [f x | f <- fs, x <- xs]
ghci> [(+), (*)] <*> [1, 2] <*> [3, 4]
[4,5,5,6,3,4,6,8]
instance Applicative IO where
pure = return
a <*> b = do
f <- a
x <- b
return (f x)
ghci> (++) <*> getLine <*> getLine
Hello
World!
"Hello World!"
A monad in X is just a monoid in the category of endofunctors of X, with product × replaced by composition of endofunctors and unit set by the identity endofunctor.
Let’s start somewhere else.
class (Applicative m) => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
x >> y = x >>= \_ -> y
return
return
is exactly like
pure
>>=
instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
Just x >>= f = f x
See Learn You a Haskell for the full example
return (0,0) >>= landLeft 1 >> landRight 4 >>= landLeft 2 >>= landLeft 1
routine = case landLeft 1 (0,0) of
Nothing -> Nothing
Just pole1 -> case landRight 4 pole1 of
Nothing -> Nothing
Just pole2 -> case landLeft 2 pole2 of
Nothing -> Nothing
Just pole3 -> landLeft 1 pole3
Remember our greet program?
greet :: IO ()
greet = do
putStr "What is your first name? "
firstName <- getLine
putStr "What is your last name? "
lastName <- getLine
putStrLn $ "Hi " ++ firstName ++ " " ++ lastName ++ "!"
return ()
greet :: IO ()
greet =
putStr "What is your first name? " >>
getLine >>= (\firstName ->
putStr "What is your last name? " >>
getLine >>= (\lastName ->
putStrLn ("Hi " ++ firstName ++ " " ++ lastName ++ "!") >>
return ()
)
)
do
is just syntactic sugar for monad operations
routine = case landLeft 1 (0,0) of
Nothing -> Nothing
Just pole1 -> case landRight 4 pole1 of
Nothing -> Nothing
Just pole2 -> case landLeft 2 pole2 of
Nothing -> Nothing
Just pole3 -> landLeft 1 pole3
routine :: Maybe (Int, Int)
routine = do
start <- return (0,0)
pole1 <- landLeft 1 start
pole2 <- landRight 4 pole1
pole3 <- landLeft 2 start
return $ landLeft 1 pole3
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
ghci> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
listOfTuples :: [(Int,Char)]
listOfTuples = do
n <- [1,2]
ch <- ['a','b']
return (n,ch)
[(n,ch) | n <- [1,2], ch <- ['a','b']]