這裏的技巧是使用升降轉換列表單子(即[a]
)爲ReaderT env []
使用lift
:
lift :: (Monad m, MonadTrans t) => m a -> t m a
或專門到你的單子堆棧:
lift :: [a] -> ReaderT [(Int,Int)] [] a
ask
返回包含在ReaderT
monad中的狀態(即[(Int, Int)]
)例如:
ask :: ReaderT [(Int, Int)] [] [(Int, Int)]
我們希望將其轉換成另一種價值相同的單子,但與類型:
??? :: ReaderT [(Int, Int)] [] (Int, Int)
所以,替代由單子,而不是在輸出跟蹤。考慮基本功能>>=
:
(>>=) :: 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)
。如果用(Int, Int)
替換lift
函數中的a
,這是一個完美匹配,這意味着表達式ask >>= lift
可以實現您想要的功能。
您遇到的另一個錯誤是ReaderT
monad的輸出類型 - 因爲它包含一個monad列表,所以您不需要將結果包裝在另一對括號中。 A 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
本身通常不是很有用),但瞭解如何理解和操縱monads和monad變換器的類型是很好的。
你可以用'Reader [(Int,Int)] [[Int]]'來代替嗎? –