如何将Reader
/ ReaderT
用于ask
作为列表类型,例如[(Int, Int)]
然后在列表monad(ask
编辑的类型)中执行计算?
我的破解代码如下,为了清楚起见缩短了:
attempt :: Int -> Int -> ReaderT [(Int,Int)] [] [[Int]]
attempt start end =
do (s0, e0) <- ask
return [0]
为了让您了解我正在尝试做什么,这里是相同的功能,使用列表monad而不是Reader:
paths :: [(Int, Int)] -> Int -> Int -> [[Int]]
paths edges start end =
if start == end
then return [end]
else do (s0, e0) <- edges
guard $ s0 == start
subpath <- paths edges e0 end
return $ s0 : subpath
我正在使用ReaderT,因为我正在学习monad变形金刚。这是使用Reader和Writer以及list monad来实现路径的更大问题的一部分。
答案 0 :(得分:8)
这里的技巧是使用lift通过使用[a]
将列表monad(即ReaderT env []
)转换为lift
:
lift :: (Monad m, MonadTrans t) => m a -> t m a
或专门用于你的monad堆栈:
lift :: [a] -> ReaderT [(Int,Int)] [] a
ask
返回包含在[(Int, Int)]
monad中的状态(即ReaderT
),例如:
ask :: ReaderT [(Int, Int)] [] [(Int, Int)]
我们希望将其转换为同一monad中的另一个值,但类型为:
??? :: ReaderT [(Int, Int)] [] (Int, Int)
因此,monad而不是输出中跟踪替代方案。考虑基本功能>>=
:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
你应该能够看到我们拥有所需的所有部分。使用ask >>= lift
:
ReaderT [(Int, Int)] [] [(Int, Int)]
,意思是a
是[(Int, Int)]
,m
是ReaderT [(Int, Int)] []
m b
为ReaderT [(Int, Int)] [] (Int, Int)
,因此b
为(Int, Int)
[(Int, Int)] -> ReaderT [(Int, Int)] [] (Int, Int)
。如果您将a
函数中的lift
替换为(Int, Int)
,则表示完全匹配,这意味着表达式ask >>= lift
可以满足您的需求。你遇到的另一个错误是ReaderT
monad的输出类型 - 因为它包含一个列表monad,你不需要将结果包装在另一对括号中。 ReaderT state []
已经包含多个结果的概念,在这种情况下,单个结果是显示图形路径的[Int]
。
以下是工作代码:
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Main where
import Control.Monad.Reader
import Control.Applicative
paths :: Int -> Int -> ReaderT [(Int,Int)] [] [Int]
paths start end = do
if start == end
then return [end]
else do
(s0, e0) <- ask >>= lift
guard $ s0 == start
(s0 :) <$> paths e0 end
input :: [(Int, Int)]
input = [(1,2), (2,7), (3,4), (7, 3), (7, 5), (5, 3)]
test :: [[Int]]
test = runReaderT (paths 2 4) input
> test
[[2,7,3,4],[2,7,5,3,4]]
我希望能够清楚地解释清楚。在这种情况下,我可能只是坚持使用原始解决方案(使用Reader
本身通常不是很有用),但知道如何理解和操作monad和monad变换器的类型是很好的。