The next three chapters are going to focus on patterns that might still seem strange and difficult. We do have reasons for introducing them now, but those reasons might not seem clear to you for a while.
Good luck.
When writing applications, programmers often need to pass around some information that may be needed intermittently or universally throughout an entire application.
Like a global configuration object that has connection strings and spark contexts and such?
import Control.Applicative
boop = (*2)
doop = (+10)
-- Function Composition, sequential
bip :: Integer -> Integer
bip = boop . doop
-- Functorial Context, sequential
bloop :: Integer -> Integer
bloop = fmap boop doop
-- Applicative Context, parallel
bbop :: Integer -> Integer
bbop = (+) <$> boop <*> doop
-- Applicative Context, parallel
duwop :: Integer -> Integer
duwop = liftA2 (+) boop doop
-- Monadic Context, parallel
boopDoop :: Integer -> Integer
boopDoop = do
a <- boop
b <- doop
return (a + b)
We can fmap
over functions apparently.
Partially applied functions can be functorial contexts.
fmap boop doop x == (*2) ((+10) x)
Partially applied functions can have Functor, Applicative, and Monad.
The Functor for functions is function composition.
The Applicative and Monad chain the argument forward in addition to composition.
(<*>) :: Applicative f => f (a -> b) -> f a -> f b
For partially applied functions, f
is (->) r
, or r ->
. Thus, the apply for functions becomes:
(<*>) :: (((->) r) (a -> b)) -> (((->) r) a) -> (((->) r) b)
-- or, more simply:
(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
This is the idea of Reader. It is a way of stringing functions together when all those functions are awaiting one input from a shared environment. ... Using Reader allows us to avoid passing that argument explicitly.
import Control.Applicative
import Data.Char
cap :: [Char] -> [Char]
cap xs = map toUpper xs
rev :: [Char] -> [Char]
rev xs = reverse xs
composed :: [Char] -> [Char]
composed = cap . rev
fmapped :: [Char] -> [Char]
fmapped = fmap cap rev
tupled :: [Char] -> ([Char], [Char])
tupled = (,) <$> cap <*> rev
tupled' :: [Char] -> ([Char], [Char])
tupled' = liftA2 (,) cap rev
tupled'' :: [Char] -> ([Char], [Char])
tupled'' = do
a <- cap
b <- rev
return (a, b)
tupled''' :: [Char] -> ([Char], [Char])
tupled''' = liftM2 (,) cap rev
tupled'''' :: [Char] -> ([Char], [Char])
tupled'''' =
cap >>= \a ->
rev >>= \b ->
return (a, b)
Reader is a way of stringing together functions that are all waiting for one input from a shared environment. It allows us to setup chains of computation that rely on some constant value provided at runtime.
Reader usually refers to the Monad instance of Functions, not Functor or Applicative.
Giving it a name helps us know the what and why of what we’re doing: reading an argument from the environment into functions. It’ll be especially nice for clarity’s sake later when we make the ReaderT monad transformer.
fmap (+1) (*2) 3
== fmap (+1) (*2) $ 3
== (fmap (+1) (*2)) 3
== ((+1) . (*2)) 3
== 7
(.) :: (b -> c) -> (a -> b) -> a -> c
-- or perhaps
(.) :: (b -> c) -> (a -> b) -> (a -> c)
-- compare to Functor
fmap :: Functor f => (a -> b) -> f a -> f b
-- or perhaps
fmap :: Functor f => (b -> c) -> f b -> f c
-- lining up with each other
fmap :: Functor f => (b -> c) -> f b -> f c
(.) :: (b -> c) -> (a -> b) -> (a -> c)
-- and replacing `f` with `a ->` for functions
fmap :: Functor (-> a) => (b -> c) -> (a -> b) -> (a -> c)
(.) :: (b -> c) -> (a -> b) -> (a -> c)
instance Functor ((->) r) where
fmap = (.)
newtype Reader r a =
Reader { runReader :: r -> a }
instance Functor (Reader r) where
fmap f (Reader ra) = Reader $ (f . ra)
ask :: Reader a a
ask = Reader $ id
pure :: a -> f a
pure :: a -> (r -> a)
(<*>) :: f (a -> b) -> f a -> f b
(<*>) :: (r -> a -> b) -> (r -> a) -> (r -> b)
newtype HumanName = HumanName String deriving (Eq, Show)
newtype DogName = DogName String deriving (Eq, Show)
newtype Address = Address String deriving (Eq, Show)
data Person =
Person {
humanName :: HumanName,
dogName :: DogName,
address :: Address
} deriving (Eq, Show)
data Dog =
Dog {
dogsName :: DogName,
dogsAddress :: Address
} deriving (Eq, Show)
mickey :: Person
mickey = Person (HumanName "Mickey")
(DogName "Pluto")
(Address "Disneyland")
-- without Reader
getDog :: Person -> Dog
getDog p = Dog (dogName p) (address p)
-- with Reader
getDogR :: Person -> Dog
getDogR = Dog <$> dogName <*> address
-- with Reader and liftA2
getDogR' :: Person -> Dog
getDogR' = liftA2 Dog dogName address
myLiftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
myLiftA2 c f1 f2 = c <$> f1 <*> f2
asks :: (r -> a) -> Reader r a
asks f = Reader $ f
{-# LANGUAGE InstanceSigs #-}
instance Applicative (Reader r) where
pure :: a -> Reader r a
pure x = Reader $ \r -> x
(<*>) :: Reader r (a -> b) -> Reader r a -> Reader r b
(Reader rab) <*> (Reader ra) =
Reader $ \r -> (rab r) (ra r)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
-- replacing `m` with `r ->` and lining up, we have:
:: m a -> (a -> m b) -> m b
:: (r -> a) -> (a -> r -> b) -> (r -> b)
getDogRM :: Person -> Dog
getDogRM = do
name <- dogName
addy <- address
return $ Dog name addy
{-# LANGUAGE InstanceSigs #-}
instance Monad (Reader r) where
return = pure
(>>=) :: Reader r a -> (a -> Reader r b) -> Reader r b
(Reader ra) >>= aRb =
Reader $ \r -> runReader (aRb (ra r)) r
It can't do anything the Applicative cannot.
Generally speaking, you cannot derive a Monad from an Applicative, even though the other way is possible. However, for Reader, we can use flip
and apply
to make the Monad instance.
The “read-only” nature of the type argument
r
means that you can swap in a different type or value ofr
for functions that you call, but not for functions that call you.
In the next chapter, we’ll see the State monad where we can not only read in a value, but provide a new one which will change the value carried by the functions that called us, not only those we called.
Reader rarely stands alone. Usually it’s one Monad in a stack of multiple types providing a Monad instance such as with a web application that uses Reader to give you access to context about the HTTP request. When used in that fashion, it’s a monad transformer and we put a letter T after the type to indicate when we’re using it as such, so you’ll usually see ReaderT in production Haskell code rather than Reader.
(see attached file for full exercises)
Final output is:
*ExerciseChapter22> main
Just [3,2,1]
[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
Just [6,9]
Just 15
Nothing
True
[True,False,False]
[True,True,False]
False
[True,False,False]
False