Note: this is the stubbed version of module GenericMonads. You should
download the lhs version of this
module and replace all parts marked undefined
.
Eventually, the complete
version will be made available.
In class exercise: General Monadic Functions
> module GenericMonads where
> import Prelude hiding (mapM, foldM, sequence)
> import Test.HUnit
> import Test.QuickCheck
Generic Monad Operations
This problem asks you to recreate some of the operations in the Control.Monad library. You should not use any of the functions defined in that library to solve this problem. (These functions also appear in more general forms elsewhere, so other libraries that are off limits for this problem include Control.Applicative
, Data.Traversable
and Data.Foldable
.)
NOTE: because these operations are so generic, the types will really help you figure out the implementation, even if you don't quite know what the function should do.
For that reason you must test each of these functions with at least two test cases, one using the Maybe
monad, and one using the List
monad. After you do that, try to describe in words what each operation does for that specific monad.
HINT: define this function by recursion on the list argument.
> mapM :: Monad m => (a -> m b) -> [a] -> m [b]
> mapM = error "mapM: unimplemented"
> testMapM :: Test
> testMapM = undefined
> foldM :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
> foldM = error "foldM: unimplemented"
> testFoldM :: Test
> testFoldM = undefined
> sequence :: Monad m => [m a] -> m [a]
> sequence = error "sequence: unimplemented"
> testSequence :: Test
> testSequence = undefined
> -- (d) Define the Kleisli "fish operator"
> --
> (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
> (>=>) = error ">=>: unimplemented"
> testKleisli :: Test
> testKleisli = undefined
For more information about this operator, see the explanation at the bottom of this page.
> join :: (Monad m) => m (m a) -> m a
> join = error "join: unimplemented"
> testJoin :: Test
> testJoin = undefined
> -- (f) Define the 'liftM' function
> liftM :: (Monad m) => (a -> b) -> m a -> m b
> liftM = error "liftM: unimplemented"
> testLiftM :: Test
> testLiftM = undefined
Thought question: Is the type of liftM
similar to that of another function we've discussed recently?
> -- (g) And its two-argument version ...
Now define a variation of liftM
, liftM2
, that works for functions taking two arguments:
> liftM2 :: (Monad m) => (a -> b -> r) -> m a -> m b -> m r
> liftM2 = error "liftM2: unimplemented"
> testLiftM2 :: Test
> testLiftM2 = undefined
> -------------------------------------------------------------------------
General Applicative Functions
Which of these functions above can you equivalently rewrite using Applicative
? i.e. for which of the definitions below, can you replace undefined
with a definition that only uses members of the Applicative
type class. (Again, do not use functions from Control.Applicative
, Data.Foldable
or Data.Traversable
in your solution.)
If you provide a definition, you should write test cases that demonstrate that it has the same behavior on List
and Maybe
as the monadic versions above.
> -- NOTE: you may not be able to define all of these, but be sure to test the
> -- ones that you do
> mapA :: Applicative f => (a -> f b) -> [a] -> f [b]
> mapA f xs = undefined
> foldA :: Applicative f => (a -> b -> f a) -> a -> [b] -> f a
> foldA = undefined
> sequenceA :: Applicative f => [f a] -> f [a]
> sequenceA = undefined
> kleisliA :: Applicative f => (a -> f b) -> (b -> f c) -> a -> f c
> kleisliA = undefined
> joinA :: (Applicative f) => f (f a) -> f a
> joinA = undefined
> liftA :: (Applicative f) => (a -> b) -> f a -> f b
> liftA f x = undefined
> liftA2 :: (Applicative f) => (a -> b -> r) -> f a -> f b -> f r
> liftA2 f x y = undefined