[英]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.