accumulating/iterating 递归和 IO 操作在类型 'IO a' 中的本质是什么?

What is the nature of accumulating/iterating recursively and IO operations within a function of type 'IO a'?

...假设 a 是某种类型,例如[Int],我们想要迭代,例如获取所有元素,对它们进行操作并在操作后打印结果,如操作发生在以下代码中:

fa :: [Int] -> [Int]
fa [] = []
fa (n:rln) = (n + 1) : fa rln

...但是在函数类型中

fb :: [Int] -> IO [Int]

...这样

main :: IO ()
main =
    do
        fb [3, 2, 6, 8] >>= print

...将按顺序打印给定列表 ([4, 3, 7, 9]) 并再次打印结果。

分化和背景

我想了解类型的本质'IO a'。

这个问题不是关于如何迭代折叠和 fmap 等现成函数的问题。

不是打印和return相同结果是否有意义的问题。

到目前为止的代码

到目前为止,我已经得到了这个“野兽”:

f :: [Int] -> IO [Int]
f ln = f' ln >>= print >>= return (f' ln)
    where
        f' :: [Int] -> IO [Int]
        f' [] = return []
        f' (n:rln) = f' rln >>= (\r -> return ((n + 1) : r))

...可以像这样在 main 中应用

main :: IO ()
main = f [3, 2, 6, 8] >>= (putStrLn . (\r -> "result=" ++ show r))

...打印:

[4,3,7,9]
result=[4,3,7,9]

对我来说,可疑的是行 f ln = f' ln >>= print >>= return (f' ln) 没有将 f' ln 的结果传递给 return。

问题

类型的本质是什么IO a

特别是:

For me, suspicious is the line f ln = f' ln >>= print >>= return (f' ln) that does not pass the result of f' ln to return.

我同意,这显然是错误的。也许你想要这个:

f ln = f' ln >>= \x -> print x >> return x

有多种其他方式可以表达同一件事。

Is it correct to say that a function of type IO a always has to return type a?

不,有几个原因。第一:IO a 类型的值不是函数!第二:IO a 动作并不总是 returns;例如 exitWith ExitSuccess 没有。最后,也是最关键的:return 不是结束 IO 动作的唯一方法,因为还有许多其他基本动作。例如,getChar 是一个 IO 动作,不会调用 return -- 它是由编译器作为原语实现的。* 一个动作类型 IO a 总是允许通过调用一些其他类型 IO a 的操作来完成自身,包括编译器原语,而不是 returning 类型 a 的值。

As we can see we can perform IO () operations in a function of type IO a - but do we really have to have such a clumsy code if we print in such functions?

我不知道。我不知道你认为笨拙的是什么。如果你能把这个问题问得更详细一些objective,我很乐意回答。

What would be the most effective implementation?

我会写main = mapM foo [3, 2, 6, 8] >>= print。如果你不允许我使用标准库中的 mapM,我会先实现它,例如作为

mapM f [] = return []
mapM f (x:xs) = do
    y <- f x
    ys <- mapM f xs
    return (y:ys)

Should we generally avoid the dual use (IO operations and recursive computations) in one function?

不,IO 和递归计算相得益彰;几乎所有 main 我写过的混合 IO 和递归。

*好吧,其实我也不知道getChar是否调用了return。它可能。但如果它确实如此,那是因为它调用了一些其他更原始的东西,它不以 return.

结尾