我检查了重复问题的可能性, 并且无法找到确切的解决方案。
我在JavaScript中编写了一些函数链代码,如下所示,并且工作正常。
var log = function(args)
{
console.log(args)
return function(f)
{
return f;
};
};
(log('1'))(log('2'))(log('3'))(log('4'));
//1
//2
//3
//4
我想做一个懒惰的评估。
或撰写功能。
var log = function(args)
{
var f0 = function()
{
return console.log(args);
};
return function(f1)
{
return function()
{
f0();
return f1;
};
};
};
var world = (log('1'))(log('2'))(log('3'))(log('4'));
console.log(world);
//should be just a function,
// but in fact
//1
//[function]
world();
//should be
//1
//2
//3
//4
// but in fact
// 2
有些事情是非常错误的。 你能解决它吗?
感谢。
当我们有
时// unit :: a -> IO a
var unit = function(x)
{
return function()
{
return x;
};
};
// bind :: IO a -> (a -> IO b) -> IO b
var bind = function(x, y)
{
return function()
{
return y(x())();
};
};
// seq :: IO a -> IO b -> IO b
var seq = function(x, y)
{
return function()
{
return x(), y();
};
};
var action = function(x)
{
return function(y)
{
return y ? action(seq(x, y)) : x();
};
};
var wrap = function(f)
{
return function(x)
{
return action(function()
{
return f(x);
});
};
};
var log = wrap(console.log);
// -- runtime --
// HACK: when `world` is modified by passing a function,
// the function will be executed.
Object.defineProperties(window,
{
world:
{
set: function(w)
{
return w();
}
}
});
我们也常常非常想要异步连锁反应。
var asyncF = function(callback)
{
setTimeout(function()
{
for (var i = 0; i < 1000000000; i++)
{
};
callback("async process Done!");
}, 0);
};
var async = wrap(asyncF(function(msg)
{
world = log(msg);
return msg;
}));
现在,
world = (log(1))(async)(log(3));
//1
//3
//async process Done!
到目前为止一切顺利,现在我们尝试使用bind
world = (log(1))
(bind((async), (log(x))));
//should be
//1
//async process Done!
//3
//in fact
//ReferenceError: x is not defined
你可以修改这项工作吗?
retrun x, y;
多重值我不明白
// seq :: IO a -> IO b -> IO b
var seq = function(x, y)
{
return function()
{
return x(), y();
};
};
正如图书馆作者所提到的那样
请注意,这在Haskell中是不可能的,因为一个函数不能返回两个结果。而且,在我的拙见中,它看起来很难看。
我同意,并且不知道这是什么
return x(), y();
多次返回值。
我在这里搜索并搜索,但找不到答案。
这是什么?
(以防万一,我会选择这个hack来获取语法)
谢谢!
答案 0 :(得分:26)
因此,如果我正确理解了这个问题,您希望在JavaScript中链接IO操作。为此,首先需要定义IO操作的内容。想到IO动作的一种方法是它只是一个不带参数的函数。例如:
// log :: a -> IO b
function log(x) {
return function () { // IO action
return console.log(x);
};
}
将IO操作表示为不带参数的函数的一个优点是它与thunks(unevaluated expressions)的表示相同。 Thunk是能够在像Haskell这样的语言中进行延迟评估的东西。因此,你可以免费得到懒惰。
现在组成。你如何在JavaScript中组成两个IO动作?在Haskell中,您使用>>
运算符对IO操作进行排序,这些操作通常按>>=
(a.k.a。bind
)定义,如下所示:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
(>>) :: Monad m => m a -> m b -> m b
x >> y = x >>= \_ -> y
在JavaScript中为IO操作编写等效的bind
函数很容易:
// 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中的作用。请注意,整个操作包含在多余的懒惰功能中。
Haskell还有一个return
函数,它将值提升为monadic上下文。由于return
是JavaScript中的关键字,因此我们将其称为unit
:
// unit :: a -> IO a
function unit(x) {
return function () {
return x;
};
}
事实证明,Haskell中还有一个sequence
运算符,它对列表中的monadic值进行排序。它可以在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")
和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
。但是,这就是你想要的。
答案 1 :(得分:7)
让我们来看看这里发生了什么:
var log = function(args)
{
var f0 = function()
{
return console.log(args);
};
return function(f1)
{
return function()
{
f0();
return f1;
};
};
};
并且内联了一点:
var log = function(args) {
return function(f1) {
return function() {
console.log(args);
return f1;
};
};
};
所以我们返回一个接受函数f
的函数f1
,并返回一个函数g
,它执行 logic 并返回{{1} }。相当满口!你的问题是为什么
f1
记录(log('1'))(log('2'))(log('3'));
。我放弃了1
因为转到3就足以显示你所描述的情况了。要回答这个问题,让我们玩编译器并进行内联游戏!
log('4')
简单替换。我接受了(log('1'))(log('2'))(log('3'))
// =>
(
function (f1) {
return function () {
console.log('1');
return f1;
}
}
)(
function (f1) {
return function () {
console.log('2');
return f1;
}
}
)(
function (f1) {
return function () {
console.log('3');
return f1;
}
}
)
的每个实例,用函数的内容替换它,用传递的值替换参数。我们再来一次!
log(something)
这个有点棘手:我扩展了第一个函数调用。最顶层的函数收到了一个参数(
function () {
console.log('1');
return function (f1) {
return function () {
console.log('2');
return f1;
}
};
}
)(
function (f1) {
return function () {
console.log('3');
return f1;
}
}
)
,我们刚刚提供了一个值,所以我进入函数并用给定的值(f1
的结果)替换了f1
的每一次出现,就像log('2')
参数一样。
如果您仍然没有关注,请再次查看此处发生的事情,但我的建议是自己做:将代码段复制到您喜欢的代码编辑器中并自行进行扩展。
现在您可以看到调用log
的原因了。我们编译器需要做的下一件事就是处理下一个函数调用。而且whadya知道,该函数的第一行是log('1')
!做得更好!
我不知道Haskell或IO Monad,但是按照你目前的计划,我认为你不能用基本功能做你想做的事,而不是那样。如果你能用这个......呃......模式说出你想解决什么问题,也许我们可以提供帮助!
答案 2 :(得分:3)
这是因为你刚回来并归还所有东西......
输出中印有三件事:
1
function ()
{
f0();
return f1;
}
2
1)第一次输出:1
这是因为:console.log(args)
只在链接中执行一次,因为f0只在最后一次执行args
时执行一次(因为返回每个嵌套函数,是什么值)你最后返回的是一个函数f1,它在args的值为1时执行f0()。
然后它将1打印到控制台。
2)第二次输出函数f1
在最后一次返回时执行的return f1;
(当你将args作为1时返回给函数)
function ()
{
f0();
return f1;
}
回到变量世界,因此只有内部嵌套函数被打印到控制台。
3)第三输出:2
然后执行函数world()
,
再次直接执行函数f1(参见world
和world()
之间只有一点点差异)但这次是将args
作为{{1}传递的返回函数}}
原因:世界只输出函数,2
将执行该函数
当您编写world()
时,在返回world()
的最后一次,args的值为2将直接执行。
我知道我的答案非常措辞......但希望这有助于(希望你明白)
答案 3 :(得分:2)
执行时
var world = (log('1'))(log('2'))(log('3'))(log('4'));
首先执行(log('1')),返回一个接受(log('2'))的函数。
此匿名函数开始执行但不接受任何参数。 log('3')被忽略了。这可以通过
验证if(typeof(arguments[0]) == 'function'){
console.log("Got a neglected argument");
console.log(arguments[0]());
}
执行f0();
(将1打印到屏幕)后,我们返回指向log('2')返回的函数的f1,这将记录log('4');
这可以通过以下方式验证:
world()()()
此输出:
2
4
undefined