首页 > 解决方案 > 为什么我们需要 IO?

问题描述

处理尴尬的小队:Haskell 中的单子输入/输出、并发、异常和外语调用中,SPJ 指出:

例如,函数式程序可能是一个将输入字符串映射到输出字符串的函数:

main :: String -> String

现在,一个用(喘气!)C 编写的“包装器”程序可以从某处获取输入字符串 [...] 将函数应用于它,并将结果存储在某处 [...]

然后他接着说,这在包装器中定位了“罪恶”,并且这种方法的问题在于一种罪恶导致了另一种罪恶(例如,多个输入、删除文件、打开套接字等)。

这对我来说似乎很奇怪。当以这种方式接近时,我会认为 Haskell 将是最强大的,甚至可能是最有用的。即输入是位于文件中的字符串,输出是新文件中的新字符串。如果输入字符串是一些与数据连接的数学表达式,而输出字符串是(非 Haskell)代码,那么您可以 Get Things Done。换句话说,为什么不总是把 Haskell 程序当作翻译器呢?(或者作为编译器,但作为翻译器,您可以将真正的 I/O 混合到最终的可执行文件中。)

不管这作为一般策略是否明智(我很欣赏我们可能想要完成的某些事情可能不会从数学开始),我真正的问题是:如果这确实是方法,我们可以避免这种IO a类型吗?我们是否需要其他语言的包装器?真的有人这样做吗?

标签: haskelliotranslate

解决方案


关键是String -> String对于程序的总体功能来说,这是一个相当糟糕的模型。

如果您正在编写一个接受并发流水线请求并同时响应每个流水线的 http 服务器,同时还将流水线中响应的写入与下一个请求的读取交错,该怎么办?这是 http 服务器工作的并发级别。

也许,只是也许,你可以把它塞进一个String -> String程序中。您可以将管道多路复用到单个通道中。但是超时呢?Web 服务器超时连接,以防止缓慢的 loris 攻击。你怎么解释呢?也许您的输入字符串会定期添加一系列时间戳,而不管其他输入如何?哦,但是接收者只从他们的接收缓冲区中读取的变体呢?你怎么知道你在等待发送缓冲区耗尽时被阻塞了?

如果你追求所有潜在的问题并将它们塞进一个String -> String程序中,你最终会发现服务器中几乎所有有趣的部分都存在于你的 haskell 程序之外。毕竟,必须进行多路复用,必须进行错误检测和报告,必须进行超时。如果你正在用 Haskell 编写一个 http 服务器,那么如果它真的是用 Haskell 编写的就好了。

当然,这并不意味着IO它当前存在的类型是最好的答案。可以对此提出合理的投诉。但它至少允许您在 Haskell 中解决所有这些问题。


推荐阅读