【发布时间】:2020-05-31 17:38:06
【问题描述】:
在 Haskell(以及其他函数式语言)中一直困扰我的一件事是,整个语言都是纯粹的,但是通过使用代表整个“现实世界”的对象(例如 IO monad)。
我想知道,是否有语言可以在不对整个世界建模的情况下处理这个问题?例如,将网络输入表示为一个字节数组,在读取网络输入时会延迟填充。
【问题讨论】:
标签: functional-programming side-effects io-monad
在 Haskell(以及其他函数式语言)中一直困扰我的一件事是,整个语言都是纯粹的,但是通过使用代表整个“现实世界”的对象(例如 IO monad)。
我想知道,是否有语言可以在不对整个世界建模的情况下处理这个问题?例如,将网络输入表示为一个字节数组,在读取网络输入时会延迟填充。
【问题讨论】:
标签: functional-programming side-effects io-monad
我从来没有真正喜欢过现实世界的类比。我认为它很受欢迎,因为大多数人第一次接触参数多态性是容器,所以他们的大脑想知道IO“包含”什么。实际上,它包含一种惰性求值的语法树数据结构,后来被解释为产生它所描述的副作用,但该数据结构不会向用户公开,除非通过更抽象的 IO 类型。
无论如何,除了 @phipsgabler 对 Haskell 以前使用的东西的出色回答之外,现在人们几乎在任何想要纯 FP 的地方都使用某种 IO 类型。但是,它是程序抽象的一种低级边缘。许多抽象都建立在它之上。
一个例子是函数式反应式编程,它有多种变体,但基本上是随着时间的推移设置事件流。 Elm 有一个命令/订阅模型。
此外,库通常会设置对其领域有意义的抽象,例如 Web 服务通常被建模为使用 Request 对象调用并返回 Response 对象的 IO 的函数。在该函数的堆栈下方,不需要副作用,您的界面只是纯类型,例如一个接受 User 对象并返回该用户配置文件的 HTML 的函数。
但在某些时候,无论您将其称为 IO 还是 Command 或 Observable,这一切都归结为将您想要的副作用的规范与实际执行分开的非常强大的想法那些影响。形式可能不同,但基本概念不会很快消失。
【讨论】:
Haskell 本身被指定使用您在 IO(monad)被发明之前描述的东西,名称为 dialogues。以下示例来自 Peyton Jones 和 Wadler,1993 年的 Imperative functional programming(IO 上的开创性论文):
type Dialogue = [Response] -> [Request]
main :: Dialogue
data Request = Putc Char | Getc
data Response = OK | OKCh Char
echo :: Dialogue
echo resps = Getc :
if (a == eof)
then []
else Putc a :
echo (drop 2 resps)
where
OKCh a = resps !! 1
前面是解释:
为 Haskell 语言指定的 I/O 系统(Hudak et al. [1992]) 基于对话,也称为惰性流(Dwelly [1989]; 奥唐奈 [1985];汤普森 [1989])。在 Haskell 中, 程序的类型为
Dialogue,是列表之间函数的同义词 对 I/O 请求列表的 I/O 响应。
并以一些困难结束:
Response 数据类型必须为每个请求的每个可能响应包含一个构造函数,并且【讨论】:
IO 和我一样老。所以不,我不能从那里的历史经验中说话:)