在“函数式编程第八章指南”中,它们定义了一个新的类IO,定义如下:
class IO {
static of(x) {
return new IO(() => x);
}
constructor(fn) {
this.$value = fn;
}
map(fn) {
return new IO(compose(fn, this.$value));
}
inspect() {
return `IO(${inspect(this.$value)})`;
}
}提交人解释说:
IO通过在函数包装器中捕获它来延迟不纯操作。因此,我们认为IO包含包装操作的返回值,而不是包装本身。这一点在of函数中是显而易见的:我们有一个IO(x),为了避免评估,IO(() => x)是必需的。
但是我不明白.of()方法是如何延迟评估的。例如,从这一节的开头开始进行定义,
// getFromStorage :: String -> (_ -> String)
const getFromStorage = key => () => localStorage[key];例如,如果我尝试创建一个像IO.of(localStorage[42])这样的新的IO.of(localStorage[42])对象,那么计算就不会延迟。localStorage[42]的值将立即计算(假设值为"foo"),然后使用{ $value: () => "foo" }创建新的IO对象。
我理解如何像调用延迟计算一样直接调用构造函数,但我不理解作者使用.of()方法意味着什么,以及如何“避免计算”。此外,作者没有在.of()的任何示例中使用IO,而是直接调用构造函数。
发布于 2019-12-31 09:17:28
为了使IO成为一元(根据那本书的monads概念),它需要一个.of方法,它可以将任意值封装在IO中。IO.of就是这么做的。由于本书实现IOs的本质是它们携带一个可以在稍后时间计算的函数,因此.of方法将传递的值封装在一个函数中。
IO.of(5)创建一个包装值5的IO实例。这就是全部。实际上,.of没有任何延迟效果的地方。
关于你在评论中的问题:
那么作者所说的“这在
of函数中很明显是什么意思:我们有一个IO( x),IO(() => x)只是为了避免计算而必需的。”
我认为理解这一评论所必需的信息就在它面前:
然而,我们并不认为它的$value是一个函数--这是一个实现细节,我们最好忽略它。..。因此,我们认为IO包含包装操作的返回值,而不是包装本身。
他的观点似乎是,从概念上讲,IO的“值”是包含的函数最终计算到的值,但是为了实现延迟的计算,它在内部存储一个未求值的函数,直到IO对一个值进行解析是必要的。
因此,您可以通过调用IO为5值创建一个IO.of(5),但在内部,它包含一个计算为5的函数,以便以后可以将该函数求值为值。
如果您想要创建一个实际延迟计算的IO,请使用构造函数并传递一个函数。
发布于 2019-12-31 09:42:55
我喜欢把IO.of(x)看作是不洁行为的起点。
例如,您可能希望读取DOM元素的内容,x将是它的id:(在“伪代码”中)
const readInput = id =>
IO
.of(id)
.map(id => document.getElementById(id))
.map(el => el.textContent)
readInput('#login');
// combine with other monads for examplehttps://stackoverflow.com/questions/59541760
复制相似问题