所以如果我正确理解这个问题,你想在 JavaScript 中链接 IO 操作。为此,您首先需要定义什么是 IO 操作。考虑 IO 操作的一种方法是,它只是一个不带参数的函数。例如:
// log :: a -> IO b
function log(x) {
return function () { // IO action
return console.log(x);
};
}
将 IO 操作表示为不带参数的函数的优点之一是,它与thunks https://stackoverflow.com/a/21441278/783743 (未计算的表达式 https://stackoverflow.com/a/19862784/783743)。 Thunk 是在 Haskell 等语言中启用惰性求值的东西。因此,你会免费获得懒惰。
现在组成。如何在 JavaScript 中组合两个 IO 操作?在 Haskell 中,你使用>>
用于对 IO 操作进行排序的运算符,通常定义为>>=
(又名bind
) 如下:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(>>) :: Monad m => m a -> m b -> m b
x >> y = x >>= \_ -> y
很容易写出等价的bind
JavaScript 中 IO 操作的函数:
// bind :: IO a -> (a -> IO b) -> IO b
function bind(x, y) {
return function () {
return y(x())();
};
}
假设你有一个 IO 操作x :: IO a
。由于它只是一个没有参数的函数,因此当您调用它时,它相当于评估 IO 操作。因此x() :: a
。将此结果提供给函数y :: a -> IO b
IO 操作的结果y(x()) :: IO b
。请注意,为了懒惰,整个操作都包装在一个多余的函数中。
同样,实现也同样简单>>
操作员。我们就这样称呼它吧seq
如“顺序”。
// seq :: IO a -> IO b -> IO b
function seq(x, y) {
return function () {
return x(), y();
};
}
这里我们评估 IO 表达式x
,不关心其结果然后返回IO表达式y
。这正是>>
运算符在 Haskell 中执行。请注意,为了懒惰,整个操作都包装在一个多余的函数中。
哈斯克尔还有一个return
将值提升到一元上下文的函数。自从return
是 JavaScript 中的一个关键字,我们称之为unit
反而:
// unit :: a -> IO a
function unit(x) {
return function () {
return x;
};
}
事实证明还有一个sequence
Haskell 中的运算符对列表中的单值进行排序。它可以在 JavaScript 中实现 IO 操作,如下所示:
// sequence :: [IO a] -> IO [a]
function sequence(array) {
return function () {
var list = array;
var length = list.length;
var result = new Array(length);
var index = 0;
while (index < length)
result[index] = list[index++]();
return result;
};
}
这就是我们所需要的。现在我们可以写:
var world = sequence([log("1"), log("2"), log("3"), log("4")]);
world();
// 1
// 2
// 3
// 4
希望有帮助。
是的,确实可以使用您的语法链接 IO 操作。但是,我们需要重新定义 IO 操作:
function action(x) {
return function (y) {
return y ? action(seq(x, y)) : x();
};
}
让我们了解一下什么是action
函数确实使用了一个例子:
// log :: a -> IO b
// log :: a -> IO r -> IO r
function log(x) {
return action(function () {
return console.log(x);
});
}
现在你可以这样做:
log("1")(); // :: b
log("1")(log("2")); // :: IO r
在第一种情况下,我们评估了 IO 操作log("1")
。在第二种情况下,我们对 IO 操作进行排序log("1")
and log("2")
.
这允许您执行以下操作:
var world = (log("1"))(log("2"))(log("3"))(log("4"));
world();
// 1
// 2
// 3
// 4
此外你还可以这样做:
var newWorld = (world)(log("5"));
newWorld();
// 1
// 2
// 3
// 4
// 5
等等....
其他一切都保持不变。请注意,这在 Haskell 中是不可能的,因为一个函数不能返回两个结果。另外,以我的愚见,它看起来很丑。我更喜欢使用sequence
反而。然而,这就是你想要的。