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