haskell - 为什么我们需要 IO?
问题描述
在处理尴尬的小队:Haskell 中的单子输入/输出、并发、异常和外语调用中,SPJ 指出:
例如,函数式程序可能是一个将输入字符串映射到输出字符串的函数:
main :: String -> String
现在,一个用(喘气!)C 编写的“包装器”程序可以从某处获取输入字符串 [...] 将函数应用于它,并将结果存储在某处 [...]
然后他接着说,这在包装器中定位了“罪恶”,并且这种方法的问题在于一种罪恶导致了另一种罪恶(例如,多个输入、删除文件、打开套接字等)。
这对我来说似乎很奇怪。当以这种方式接近时,我会认为 Haskell 将是最强大的,甚至可能是最有用的。即输入是位于文件中的字符串,输出是新文件中的新字符串。如果输入字符串是一些与数据连接的数学表达式,而输出字符串是(非 Haskell)代码,那么您可以 Get Things Done。换句话说,为什么不总是把 Haskell 程序当作翻译器呢?(或者作为编译器,但作为翻译器,您可以将真正的 I/O 混合到最终的可执行文件中。)
不管这作为一般策略是否明智(我很欣赏我们可能想要完成的某些事情可能不会从数学开始),我真正的问题是:如果这确实是方法,我们可以避免这种IO a
类型吗?我们是否需要其他语言的包装器?真的有人这样做吗?
解决方案
关键是String -> String
对于程序的总体功能来说,这是一个相当糟糕的模型。
如果您正在编写一个接受并发流水线请求并同时响应每个流水线的 http 服务器,同时还将流水线中响应的写入与下一个请求的读取交错,该怎么办?这是 http 服务器工作的并发级别。
也许,只是也许,你可以把它塞进一个String -> String
程序中。您可以将管道多路复用到单个通道中。但是超时呢?Web 服务器超时连接,以防止缓慢的 loris 攻击。你怎么解释呢?也许您的输入字符串会定期添加一系列时间戳,而不管其他输入如何?哦,但是接收者只从他们的接收缓冲区中读取的变体呢?你怎么知道你在等待发送缓冲区耗尽时被阻塞了?
如果你追求所有潜在的问题并将它们塞进一个String -> String
程序中,你最终会发现服务器中几乎所有有趣的部分都存在于你的 haskell 程序之外。毕竟,必须进行多路复用,必须进行错误检测和报告,必须进行超时。如果你正在用 Haskell 编写一个 http 服务器,那么如果它真的是用 Haskell 编写的就好了。
当然,这并不意味着IO
它当前存在的类型是最好的答案。可以对此提出合理的投诉。但它至少允许您在 Haskell 中解决所有这些问题。
推荐阅读
- grpc - 将 retryPolicy 与 python GRPC 客户端一起使用
- r - 只为绘图选择一小组值
- reporting-services - 每个数据集的 SSRS 滑块
- javascript - chrome扩展中的异步调用问题
- c# - 反序列化具有 2 种类型字段且一种类型是递归的 Json
- substrate - POe Decentralize App - 使用未声明类型或模块 sp/std
- cassandra - Cassandra 故障转移策略
- javascript - 上一页箭头弄乱了图像宽度
- android - 将 Firebase 分析注入 apk
- r - ggplot 可以围绕每个图例条目绘制轮廓吗?