What I would like to do is make an Applicative Functor out of the Reader monad that does something like this:
data MyData = Int Int
get2Sum :: Reader [Int] Int
get2Sum = do
myData <- ask
let fst2 = take 2 myData
case length fst2 of
2 -> return $ sum fst2
_ -> return 0
myDataFromApplicative = MyData <$> get2Sum <*> get2Sum
main = print $ runReader myDataFromApplicative [1,2]
However, if run something like
runReader myDataFromApplicative [1]
Instead of giving me MyData 0 0
I want it to give me Error
I was playing around with creating my own Reader Monad to accomplish this, but couldn't quite figure it out.
What I imagine is something like this (obviously this is just an outline
data SuccessReader r a = Interm {runSuccessReader :: r -> SuccessReader a} | Success a | Error
throwError :: SuccessReader ()
get2Sum :: Reader [Int] Int
get2Sum = do
myData <- ask
let fst2 = take 2 myData
case length fst2 of
2 -> return $ sum fst2
_ -> throwError
myDataFromApplicative = MyData <$> get2Sum <*> get2Sum
main = do
print $ runSuccessReader myDataFromApplicative [1,2]
print $ runSuccessReader myDataFromApplicative [1]
which would output
Success MyData 3 3
Error
A functor is a data type that implements the Functor typeclass. An applicative is a data type that implements the Applicative typeclass. A monad is a data type that implements the Monad typeclass. A Maybe implements all three, so it is a functor, an applicative, and a monad.
Every Monad is an Applicative Just as IO , every monad can be made into an applicative functor.
Definition. In Haskell, an applicative is a parametrized type that we think of as being a container for data of that type plus two methods pure and <*> . Consider a parametrized type f a . The pure method for an applicative of type f has type. pure :: a -> f a.
Another simple example of a functor is the Maybe type. This object can contain a value of a particular type as Just , or it is Nothing (like a null value).
You don't need to write your own monad, as this is exactly the problem that monad transformers and monad stacks solve. Since you want a combination of a Reader and Maybe, you can use the ReaderT transformer with the Maybe monad. E.g.
get2Sum :: ReaderT [Int] Maybe Int
get2Sum = do
myData <- ask
let fst2 = take 2 myData
case length fst2 of
2 -> return $ sum fst2
_ -> lift Nothing
The type of get2Sum means that we have the outer monad Reader [Int] which contains the inner monad Maybe. In the implementation of get2Sum, lift is used to run operations in the inner monad (in this case, simply signalling error with Nothing). Now when you run (note the T in runReaderT)
main = do
print $ runReaderT myDataFromApplicative [1,2]
print $ runReaderT myDataFromApplicative [1]
you get
Just (MyData 3 3)
Nothing
You could also hide the monad stack inside a custom newtype
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
import Control.Applicative
import Control.Monad.Reader
data MyData = MyData Int Int deriving Show
newtype MyMonad a = MyMonad (ReaderT [Int] Maybe a)
deriving (Functor, Applicative, Monad, MonadReader [Int])
runMyMonad :: MyMonad a -> [Int] -> Maybe a
runMyMonad (MyMonad m) = runReaderT m
myError :: MyMonad a
myError = MyMonad $ lift Nothing
get2Sum :: MyMonad Int
get2Sum = do
myData <- ask
let fst2 = take 2 myData
case length fst2 of
2 -> return $ sum fst2
_ -> myError
myDataFromApplicative = MyData <$> get2Sum <*> get2Sum
main = do
print $ runMyMonad myDataFromApplicative [1,2]
print $ runMyMonad myDataFromApplicative [1]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With