[英]Haskell sequencelistIO [a -> IO a] -> a -> IO a
我有以下問題:我有一個任務來編寫一個函數,該函數接受IO交互的列表和初始值。 第一個動作將初始值作為參數,函數應將其結果(IO a)作為參數傳遞給下一個交互。
后者需要a類型的數據,而不是IO a類型的數據。 我不知道如何克服這個障礙。
這是我所擁有的:
seqList :: [a -> IO a] -> a -> IO a
seqList [] n = return n
seqList (inter:inters) n = do
val <- inter n
return $ seqList inters val
問題是val是(IO a)類型,但下一個IO期望a。 我嘗試了類似的東西
tmp <- unsafePerformIO val
val <-之后
但這無濟於事,這真的是很糟糕的風格。 我怎么解決這個問題?
我想要提示,沒有解決辦法,
提前致謝。
編輯
我通過以下方式對其進行了編輯:
seqList :: [a -> IO a] -> a -> IO a
seqList [] n = return n
seqList (inter:inters) n = do
val <- inter n
seqList inters val
因為seqList inter val已經是正確的類型。
這應該可以,還是我弄錯了? 它實際上適用於我的示例。
我似乎對整個do-monads-io-stuff還是很陌生的。
非常感謝您的提示。
編輯的版本正確。
不過,有一種有趣的方式可以解決這個問題。 一個人可以如下分析類型
type Thing a = a -> IO a
seqList :: [Thing a] -> Thing a
換句話說, seqList
是一種組合Thing
的機制。 如果我們稍微重寫您的工作代碼,我們可以強調這一點。
seqList :: [Thing a] -> Thing a
seqList [] n = neutralThing n
seqList (thingHere : restOfThings) n = do
let remainingThing = seqList restOfThings
val <- thingHere
remainingThing val
neutralThing :: Thing a
neutralThing a = return a
特別是,我將三部分分開
do
注釋位“組合”事物。 我們可以走得更遠
seqList :: [Thing a] -> Thing a
seqList [] = neutralThing
seqList (thingHere : restOfThings) =
combineThings thingHere (seqList restOfThings)
neutralThing :: Thing a
neutralThing a = return a
combineThings :: Thing a -> Thing a -> Thing a
combineThings thing1 thing2 n = do
n' <- thing1 n
n'' <- thing2 n'
return n''
現在我們可以識別出一個通用的模式: seqList
只是列表的一個折疊。
seqList :: [Thing a] -> Thing a
seqList = foldr combineThings neutralThing
如果我們認識到,褶皺經常暴露Monoid
小號我們還可以發現如何Thing a
是對任何一個選擇半群a
memptyThing :: Thing a
memptyThing = neutralThing
mappendThing :: Thing a -> Thing a -> Thing a
mappendThing = combineThings
最后,如果我們真的很聰明,我們可以注意到Thing
繼承了Category
一般性構造,尤其是一種稱為Kleisli IO
category的東西。 如果要使用Kleisli
類型本身,則會有很多包裝和展開,但是我們可以檢查Control.Monad
的return
和(>=>)
類型。
return :: Monad m => a -> m a
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)
稍加注意,我們可以看到這些類型與memptyThing
和mappendThing
兼容。 因此,針對您的問題的最終解決方案如下
seqList :: [Thing a] -> Thing a
seqList = foldr (>=>) return
最后我們可以注意到,如果我們喜歡的話,可以使用更通用的類型
seqList :: Monad m => [a -> m a] -> (a -> m a)
seqList = foldr (>=>) return
另一種思考的方式是:如果您有兩個這樣的動作,如何將它們鏈接在一起? Control.Monad
庫中有一個運算符可以執行此操作 。 應該不難理解:
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c
f >=> g = \a -> do
b <- f a
g b
而且,如果您具有該運算符,則可以通過獲取動作列表並基本上在所有動作之間放置>=>
來編寫seqList
。 標准的文件foldr
功能 ,可以解決問題; 正如文檔所述,它確實做到了:
foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z)...)
因此,把那些一起,再加上return
的空單時,你會得到:
import Control.Monad ((>=>))
seqList :: [a -> IO a] -> a -> IO a
seqList actions = foldr (>=>) return actions
可以通過以下等式描述誰的行為:
foldr (>=>) return [] == return
foldr (>=>) return [x1, ..., xn] == x1 >=> ... >=> xn >=> return
讓我們更詳細地解決它! foldr
的定義是這樣的:
foldr :: (a -> b -> b) -> b -> [a] -> b
foldr _ z [] = z
foldr f z (x:xs) = f x (foldr f z xs)
因此,使用該代碼,我們可以這樣重寫seqList
定義:
-- Use the definition of `foldr` to split this into two cases
seqList [] = return
seqList (action:actions) = action >=> foldr (>=>) return actions
-- Use the definition of `>=>` to spell out the second equation
seqList [] = return
seqList (action:actions) = \a -> do
val <- action a
foldr (>=>) return actions val
-- But by the definition of `seqList`, we can rewrite the last line
-- to this:
seqList [] = return
seqList (action:actions) = \a -> do
val <- action a
seqList actions val
這就是您第二次嘗試時寫的!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.