Skip to content

Instantly share code, notes, and snippets.

@Woody88
Last active November 19, 2018 08:35
Show Gist options
  • Select an option

  • Save Woody88/ffc6386794cf3a866e6aab13c9adcd45 to your computer and use it in GitHub Desktop.

Select an option

Save Woody88/ffc6386794cf3a866e6aab13c9adcd45 to your computer and use it in GitHub Desktop.
module Reader1 where
data Reader r a = Reader { runReader :: r -> a }
instance Functor (Reader r) where
-- fmap :: (a -> b) -> Reader r a -> Reader r b
-- fmap :: (a -> b) -> (r -> a) -> (r -> b)
-- I can compose the body
-- fmap f (Reader ra) = Reader $ \r -> f (ra r)
fmap f (Reader ra) = Reader $ f . ra
instance Applicative (Reader r) where
{-- pure :: a -> Reader r a
pure :: a -> (r -> a)
very similar to the const type definition. It also makes sense that the const function
should work. The purpose of the reader function is to never modify the r value. const never
touches the second argument.
const :: a -> b -> a
const :: a -> (r -> a) -- same as pure function
pure x = Reader $ \r -> x -- original function
--}
pure = Reader . const
{-- (<*>) :: Reader r (a -> b) -> Reader r a -> Reader r b
(<*>) :: r -> (a -> b) -> (r -> a) -> (r -> b)
I can use applicative for the body
f :: r -> a -> b
ra :: r -> a
both need a reader, hence applicative is what we are looking for.
(Reader f) <*> (Reader ra) = Reader $ \r -> f r (ra r)
--}
(Reader f) <*> (Reader ra) = Reader $ f <*> ra
instance Monad (Reader r) where
{-- return :: a -> Reader r a
return :: a -> (r -> a)
--}
return = Reader . const
{-- (>>=) :: Reader r a -> (a -> Reader r b) -> Reader r b
(>>=) :: (r -> a) -> (a -> r -> b) -> (r -> b)
The denition below will force us to define Applicative even if we don't need!
(Reader ra) >>= f = Reader $ (flip runReader) <*> (f . ra)
--}
(Reader ra) >>= f = Reader $ \r -> runReader (f . ra $ r) r
{-- ask :: Reader a a
ask :: Reader (a -> a)
It needs a variable that accepts reader/env and return it to the caller.
This looks a lot like the id function
--}
ask :: Reader a a
ask = Reader id
testFunctor :: IO ()
testFunctor = do
putStrLn $ runReader intMessage 0
putStrLn $ runReader intMessage 1
putStrLn $ runReader ((++"!!") <$> intMessage) 0
testApplicative :: IO ()
testApplicative = do
putStrLn $ runReader (pure "Zero") 0
putStrLn $ runReader (addBangMark <*> intMessage) 0
testMonad :: IO ()
testMonad = do
putStrLn $ runReader (return "Zero") 0
putStrLn $ runReader ( (return . (++"!!")) =<< intMessage) 0
testAsk :: IO ()
testAsk = do
putStrLn $ runReader testAsk' 0
putStrLn $ runReader testAsk' 1
{-- using the ask method with reader requires the developer to use pure when return the last value.
When implicity calling the reader with a function like ask we are now computing directly inside
of the Reader context. As oppose of explicity calling the reader by expressing the Reader constructor
and the function, we computing within the reader function (r -> a) which doesn't require one to use
pure because the Reader constructor will return the right context.
--}
testAsk' :: Reader Int String
testAsk' = do
val <- ask
case val of
0 -> pure "Zero"
_ -> pure "Random Number"
intMessage :: Reader Int String
intMessage = Reader $ \r ->
case r of
0 -> "Zero"
_ -> "Random Number"
addBangMark :: Reader Int (String -> String)
addBangMark = Reader $ \r -> (++ "!!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment