繁体   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