Javascript 通过在函数中包装参数来延迟计算?

Javascript 通过在函数中包装参数来延迟计算?,javascript,functional-programming,monads,Javascript,Functional Programming,Monads,在《函数式编程指南》中,他们定义了一个新类,IO,定义如下: 类IO{ (x)的静态特性{ 返回新IO(()=>x); } 建造师(fn){ 这。$value=fn; } 地图(fn){ 返回新IO(组合(fn,this.$value)); } 检查(){ 返回'IO(${inspect(this.$value)})`; } } 作者解释说: IO通过在函数包装器中捕获不纯操作来延迟该操作。作为 因此,我们认为IO包含包装的 而不是包装器本身。这一点在《金融时报》中很明显 功能:我们有一个IO

在《函数式编程指南》中,他们定义了一个新类,
IO
,定义如下:

类IO{
(x)的静态特性{
返回新IO(()=>x);
}
建造师(fn){
这。$value=fn;
}
地图(fn){
返回新IO(组合(fn,this.$value));
}
检查(){
返回'IO(${inspect(this.$value)})`;
}
}
作者解释说:

IO
通过在函数包装器中捕获不纯操作来延迟该操作。作为 因此,我们认为
IO
包含包装的 而不是包装器本身。这一点在《金融时报》中很明显 功能:我们有一个
IO(x)
IO(()=>x)
只是为了避免 评估

但是我对()的
.of
方法如何延迟评估感到困惑。例如,从本节的开头脱离定义

//getFromStorage::String->(u->String)
const getFromStorage=key=>()=>localStorage[key];
例如,如果我尝试创建一个新的
IO
对象,比如
IO.of(localStorage[42])
,这根本不会延迟评估。
localStorage[42]
的值将立即求值(假设其求值为
“foo”
),然后使用
{$value:()=>“foo”}
创建新的
IO
对象


我理解直接调用构造函数是如何延迟计算的,但我不理解作者使用
.of()
方法意味着什么,以及如何“避免计算”。此外,作者在
IO
的任何示例中都没有使用
.of()
,而是直接调用构造函数。

为了使
IO
是一元的(根据那本书的单子概念),它需要一个
.of
方法,可以在
IO
中封装任意值<代码>IO.of的执行此操作。由于本书实现的
IO
s的本质是它们携带了一个可以在以后某个时间进行计算的函数,因此
.of
方法将传递的值封装在函数中

IO.of(5)
创建一个封装值
5
IO
实例。仅此而已。关于的
,实际上并没有什么会延迟效果

关于您在评论中提出的问题:

那么作者所说的“这在
函数的
中很明显:我们有一个IO(x),IO(()=>x)只是为了避免计算而必需的”是什么意思呢

我认为理解该评论所需的信息就是它前面的内容:

然而,我们不认为它的$value是一个函数——这是一个实现细节,我们最好忽略它。。。因此,我们认为IO包含包装动作的返回值,而不是包装本身

他的观点似乎是,从概念上讲,
IO
的“值”是包含的函数最终求值的值,但为了实现延迟求值,它在内部存储未求值的函数,直到
IO
需要解析为值为止

因此,您可以通过调用
IO.of(5)
为值
5
创建一个
IO
,但在内部,它包含一个计算结果为
5
的函数,以便稍后可以将此函数计算为值


如果您想要创建一个实际延迟某些不纯效果评估的
IO
,请使用构造函数并向其传递一个函数。

我喜欢将
IO.of(x)
作为不纯操作的起点

例如,您可能想要读取DOM元素的内容,
x
将是它的id:(在“伪代码”中)

const readInput=id=>
木卫一
。of(id)
.map(id=>document.getElementById(id))
.map(el=>el.textContent)
readInput(“#登录”);
//例如,与其他单子组合

对于单子
of
意味着将纯值放入相应单子的上下文中,在这种情况下,
IO
(更好的
延迟
)。这意味着没有什么可以推迟了。当您将该值放入上下文中时,该值已被计算。这样做仍然是明智的,因为您现在可以在
IO
monad中使用这个纯值了。@bob啊,这是有道理的。那么作者所说的“这在
函数的
中很明显:我们有一个
IO(x)
IO(()=>x)
只是为了避免评估,这是什么意思呢?”我们不再用这个纯值来避免评估,因为,正如你所说,它已经被评估过了。我想Brian只是想说明另一点,也就是说,我们不应该把
IO
看作是一个空函数。在这一点上,要理解他的想法有点困难。这实际上是一个很好的例子,为什么我们需要
of
作为monad API的一部分,而不仅仅是相应的构造函数。它们可以与
IO
不同。这很有道理,谢谢!因此,澄清一下,关键的区别在于,对于
.of
,在评估过程中确实没有延迟,因为传递给
of
的任何内容都会立即评估并存储。然而,
IO
的API的一个关键部分是,它的内部值是一个可以评估的函数。因此,为了实现这一点,我们只需将任何内容包装在一个函数中,以允许我们对任何类型的对象执行
IO
?@rb612是的,没错。据我所知,各种monad类型上的
.of
方法很少打算直接使用,但它的存在是为了让您可以创建与任何monad类型一起工作的抽象(例如,与
IO同样工作的
flatMap
函数)