Use list monad inside monad transformer type classes?
So, the basic problem with doing this within the requirements of the question is that there is no MTL library function for lifting from a list monad that may be arbitrary levels down. However, you can "cheat" a bit: The MonadPlus
instance for the combined Monad
is inherited from the underlying list monad regardless of the depth, and you can use it to generate the needed action:
do (s0, e0) <- ask >>= msum . map return
There is then also an error in the type signature, which needs to be changed to:
pathImplicitStack' :: (MonadReader [(Int, Int)] m, MonadWriter [Int] m, MonadPlus m) => Int -> Int -> m ()
EDIT: Actually on second thought this is not actually cheating. It's just using the MonadPlus
API for chaining alternative actions instead of using the underlying list monad directly.
I think the difficulty here is that you're mixing up layers of the monad. Looking at
pathImplicitStack' :: (MonadReader [(Int, Int)] m, MonadWriter [Int] m, MonadPlus m) => Int -> Int -> [m ()]
This function returns a list of m ()
computations, however the ask >>= lift
(and your earlier question) are presuming that List
is the base monad upon which you stack extra transformers. If you want to use List
as the base monad, you'll need to change the type of pathImplicitStack'
pathImplicitStack' :: (MonadReader [(Int, Int)] (t []), MonadWriter [Int] (t []), MonadPlus (t [])) => Int -> Int -> t [] ()
But even this isn't quite general enough, because this only allows adding a single transformer on top of the List. You could use a type-operator library to combine two monad transformers into a single transformer, but that seems a bit complicated for this.
Here is one option: use Identity as the base monad and perform your list operations outside that monad stack. (warning, all code untested, it may not even compile)
pathImplicitStack' :: (MonadReader [(Int, Int)] m, MonadWriter [Int] m, MonadPlus m) => Int -> Int -> m ()
pathImplicitStack' start end | start == end = tell [end]
pathImplicitStack' start end =
do (s0, e0) <- ask >>= lift
edges <- filter ((== start) . fst) <$> ask
let m (s0,e0) = tell [s0] >> pathImplicitStack' e0 end
mapM_ m edges
There is another option. You can use ListT
or LogicT
as the outer transformer, letting you use this function (or something like it):
pathImplicitStack'2 :: (MonadReader [(Int, Int)] m, MonadWriter [Int] m) => Int -> Int -> ListT m ()
pathImplicitStack'2 start end | start == end = tell [end]
pathImplicitStack'2 start end =
do (s0, e0) <- ask
guard $ s0 == start
tell [start]
pathImplicitStack'2 e0 end
-- if you have MonadReader/MonadWriter instances for ListT in scope, I think this will
-- work. But if they aren't available, you will need to use `lift ask` and
-- `lift (tell ...)`
I would almost certainly choose the first approach.