在monad变换器类型类中使用list monad?

时间:2014-06-12 23:43:09

标签: haskell monad-transformers

我的目标是创建一个在ReaderT WriterT堆栈或RWS堆栈中使用列表monad的函数。更一般地说,如何在mtl类型类中使用列表monad,例如MonadReader,MonadWriter?

为什么我要这样做?这个问题是Beginning Haskell中的练习。它要求我使用包含基本列表monad的MonadReader和MonadWriter的功能。要检查该功能是否通用,请使用两个不同的monad来[测试]所请求的功能:ReaderT r (WriterT w []) aRWST r w s m a"所以这本书暗示这是可能的。

我无法弄清楚如何告诉'编译器使用列表monad。如果我使用ask >>= liftask >>= lift . lift我可以使用2级堆栈(RWST [])或3级堆栈(ReaderT WriterT [])来工作,但不能同时工作。

我的问题的焦点:

pathImplicitStack' start end | start == end = tell [end]
pathImplicitStack' start end =
  do  (s0, e0) <- ask >>= lift
      guard $ s0 == start
      tell [start]
      pathImplicitStack' e0 end

此外,我想知道如何输入该功能。到目前为止,我最好的尝试看起来像pathImplicitStack' :: (MonadReader [(Int, Int)] m, MonadWriter [Int] m, MonadPlus m) => Int -> Int -> m ()我知道这不对,列表monad可能会丢失。另外,我认为MonadPlus在类型签名中可能很有用,但我不太确定。

这一行:do (s0, e0) <- ask >>= lift是给我带来麻烦的。我尝试过0,1和2次升降机没有成功。我希望ask获得[(Int, Int)],然后使用列表monad来处理(Int, Int)(并让列表monad为我尝试所有可能性)。

作为练习的一部分,我需要能够使用这两个函数(或非常相似的函数)调用pathImplicitStack'

pathImplicitRW :: [(Int, Int)] -> Int -> Int -> [[Int]]
pathImplicitRW edges start end = execWriterT rdr
  where rdr = runReaderT (pathImplicitStack' start end) edges :: WriterT [Int] [] ()

pathImplicitRWS :: [(Int, Int)] -> Int -> Int -> [[Int]]
pathImplicitRWS edges start end = map snd exec
  where exec = execRWST (pathImplicitStack' start end) edges ()

这与我之前的问题有关:How do I use list monad inside of ReaderT?

整个文件易于测试:

{-# LANGUAGE FlexibleContexts #-}

module Foo where

import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.RWS

graph1 :: [(Int, Int)]
graph1 = [(2013,501),(2013,1004),(501,2558),(1004,2558)]


pathImplicitRW :: [(Int, Int)] -> Int -> Int -> [[Int]]
pathImplicitRW edges start end = execWriterT rdr
  where rdr = runReaderT (pathImplicitStack' start end) edges :: WriterT [Int] [] ()

pathImplicitRWS :: [(Int, Int)] -> Int -> Int -> [[Int]]
pathImplicitRWS edges start end = map snd exec
  where exec = execRWST (pathImplicitStack' start end) edges ()

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
      guard $ s0 == start
      tell [start]
      pathImplicitStack' e0 end

修改

基于John L的反馈,我试过

pathImplicitStack' :: (MonadReader [(Int, Int)] (t []), MonadWriter [Int] (t []), MonadPlus (t []), MonadTrans t) => Int -> Int -> t [] ()
pathImplicitStack' start end | start == end = tell [end]
pathImplicitStack' start end =
  do  (s0, e0) <- ask >>= lift
      guard $ s0 == start
      tell [start]
      pathImplicitStack' e0 end

但正如他所指出的,它只能用于一个monad变换器来包装列表monad,即RSWT,并且不能与ReaderT WriterT一起使用。所以这不是我要找的解决方案。

2 个答案:

答案 0 :(得分:3)

因此,在问题的要求范围内执行此操作的基本问题是没有MTL库函数用于从可能是任意级别的列表monad中提取。但是,您可以“作弊”:合并MonadPlus Monad实例继承自基础列表monad,无论深度如何,您可以使用它生成所需的操作:

  do  (s0, e0) <- ask >>= msum . map return

类型签名中也存在错误,需要更改为:

pathImplicitStack' :: (MonadReader [(Int, Int)] m, MonadWriter [Int] m, MonadPlus m) => Int -> Int -> m ()
编辑:实际上,第二个想法实际上并不是在作弊。它只是使用MonadPlus API来链接替代操作,而不是直接使用基础列表monad。

答案 1 :(得分:2)

我认为这里的困难在于你混合了monad的层次。看着

pathImplicitStack' :: (MonadReader [(Int, Int)] m, MonadWriter [Int] m, MonadPlus m) => Int -> Int -> [m ()]

此函数返回m ()计算列表,但ask >>= lift(以及您之前的问题)假设List是堆叠额外变换器的基本monad。如果您想使用List作为基本monad,则需要更改pathImplicitStack'的类型

pathImplicitStack' :: (MonadReader [(Int, Int)] (t []), MonadWriter [Int] (t []), MonadPlus (t [])) => Int -> Int -> t [] ()

但即使这样也不够通用,因为这只允许在List的顶部添加一个变换器。你可以使用一个类型操作符库将两个monad变换器组合成一个单独的变换器,但这似乎有点复杂。

这是一个选项:使用Identity作为基本monad并在monad堆栈之外执行列表操作。 (警告,所有代码未经测试,甚至可能无法编译)

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

还有另一种选择。您可以使用ListTLogicT作为外部变换器,让您使用此功能(或类似的功能):

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 ...)`

我几乎肯定会选择第一种方法。