簡體   English   中英

Haskell sequencelistIO [a-> IO a]-> a-> IO a

[英]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.Monadreturn(>=>)類型。

return :: Monad m => a -> m a
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c)

稍加注意,我們可以看到這些類型與memptyThingmappendThing兼容。 因此,針對您的問題的最終解決方案如下

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

這就是您第二次嘗試時寫的!

一些提示:

  • 考慮一下列表中恰好有2個函數的情況,並查看>=>
  • 看一下Endo monoid,特別是其mconcat的簽名。 嘗試在簽名中用a -> a替換Endo a
  • Endo單子泛化的實例將如何

     newtype EndoM ma = EndoM { appEndoM :: a -> ma } 

    看起來像? 它的memptymappend什么? 它的mconcat什么?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM