您的類型簽名表示您的函數是純的(即它需要一個Int並返回一個String),但在裏面,您正在執行IO! Haskell不會讓你編寫這樣的函數。你從文件中讀取的任何內容都會永久卡在IO monad中,這就是(當然,除了不安全的函數)。
在這種情況下,結果並不那麼糟糕,因爲Yesod是一個嚴重的基於IO的框架。所有的網絡流量都停留在IO monad中!
當你在一個monad變壓器堆棧中時,你可以在堆棧的每個級別訪問monadic計算,但只有其中一個直接。您可以使用lift
將計算從堆棧中的單層移動到已轉換的monad。如果IO
位於堆棧中,無論層數有多少,都可以通過liftIO
直接訪問其操作。
所以,如果你有type T = ReaderT String IO
那麼你可能有一個功能foo :: Int -> T String
。在此函數中,您將在T
monad中操作,它將IO
monad與Reader
monad功能進行轉換。在這種情況下,您可以說lift readFile
,而不是獲得IO String
結果,您將得到T String
結果!這只是IO String
包裝的ReaderT
類型,所以不要認爲我們做了任何棘手的事情,比如逃脫IO
monad。這可能是一個有點混亂,所以讓我們來看一個例子:
import Control.Monad.Reader (ReaderT)
import Control.Monad.Writer (WriterT)
import Control.Monad.Trans (lift, liftIO)
type T = ReaderT String IO
getSortedMiddleElement :: Int -> IO String
foo :: Int -> T String
foo n = do
str <- lift $ getSortedMiddleElement n --str holds a pure String now
lift $ putStrLn str --get `putStrLn` from IO and pass the String
return str --let's wrap it back in T now
但是,如果我們不止一層離IO?讓我們試試看:
type W = WriterT String T -- WriterT String (ReaderT String IO)
-- This doesn't work; lift only gives you access to the next layer's actions
-- but IO is now more than one layer away!
--
--bar n = do
-- str <- lift $ getSortedMiddleElement n
-- Instead, we need liftIO, which will access IO across many transformer layers
bar :: Int -> W String
bar n = do
str <- liftIO $ getSortedMiddleElement n
liftIO $ putStrLn str
return str
一種可能性是將類型更改爲「Int - > IO String」,然後像處理該函數一樣處理,例如, 'readFile'或任何其他IO功能。 – kqr
謝謝,這聽起來不錯!我該如何處理'getTestR :: Int - > Handler RepPlain',它會返回HTTP響應?'getTestR :: Int - > IO Handler RepPlain'不起作用:( – sibbl
我希望我可以告訴你,但我絕不是Yesod的專家。:( – kqr