IO序列 Haskell

3

我有这样一个函数:

sequence :: [IO a] -> IO [a]
sequence [] = pure []
sequence (op:ops) = do
    x <- op
    xs <- sequence ops
    return (x:xs)

这个函数只写了一系列的IO操作。

问题在于,我想编写同样的函数,但是完全不使用'do notation',只使用运算符 >> 和 >>=。

我已经有这个版本:

mySequence:: [IO a]-> IO [a]
mySequence [] = pure []
mySequence (op:ops) =
    op >> sequence ops

但是,例如对于输入[pure 1,pure 2],它无法运行。

有人能帮我解决这个问题吗?

提前致谢。


do 版本中,你有两个绑定和一个返回语句。在没有 do 的版本中,你使用 >>,它会丢弃其左操作数的结果,你唯一返回的是 []do 版本还使用了 : 运算符,而没有 do 的版本则没有。 - sepp2k
1个回答

3
在应用符号表示法中,它非常简单:
sequence (op:ops) = (:) <$> op <*> sequence ops

在单子符号表示法中,只需翻译do并保持其结构:
sequence (op:ops) =
   op             >>= \x ->
   sequence ops   >>= \xs ->
   return (x:xs)

大致上,x <- action; ... 变成了 action >>= \x -> ...。请注意,上述 lambda 表达式的作用域延伸到表达式的最后。在显式括号中:
sequence (op:ops) =
   op >>= (\x -> sequence ops >>= (\xs -> return (x:xs)))

谢谢你的回答。 如果我们这样调用函数:sequence [ putStr "hi" , pure 4], 为什么会出错呢? - Antman
1
@AntonioSerrano [ putStr "hi", pure 4] 的类型会是什么?putStr "hi"pure 4 的类型不兼容,你不能将它们放在同一个列表中(除非你创建一个 instance Num ())。 - sepp2k
好的,我明白了,但是为什么使用示例[pure 1,pure 2]仍然不起作用?@sepp2k - Antman
@AntonioSerrano sequence [pure 1, pure 2] 会生成 pure [1, 2]。你为什么说它不起作用? - sepp2k
首先,尝试这个:sequence (op:ops) = op >>= (\x -> sequence ops >>= (\xs -> return (x:xs))) 用 sequence [pure 1, pure 2] ,控制台会显示“main: main.hs:7:1-78: Non-exhaustive patterns in function mysequence”。 其次,当我解决了这个问题后,使用该示例时,控制台没有显示任何内容,这就是为什么我说它不起作用的原因。 你得到了什么结果?谢谢@sepp2k - Antman
@AntonioSerrano 我在 GHCi 中输入 sequence [pure 1, pure 2] 并得到 [1, 2] 的结果(因为 GHCi 自动解包 IO)。当你在程序中执行类似于 main = sequence [pure 1, pure 2]; return () 的操作时,什么也不会发生,因为该序列没有副作用 - 毕竟所有元素都是纯的。 - sepp2k

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接