JavaScript链接的函数组成

19 javascript node.js

我检查了重复问题的可能性,但找不到确切的解决方案.

我在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
Run Code Online (Sandbox Code Playgroud)

我想做这个懒惰的评估.

或者撰写功能.

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
Run Code Online (Sandbox Code Playgroud)

有些事情是非常错误的.你能修好它吗?

谢谢.

这个问题已经解决,但还有进一步的问题

注释讨论中显示的异步问题

当我们有

// 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();
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

我们也常常非常想要异步连锁反应.

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;
}));
Run Code Online (Sandbox Code Playgroud)

现在,

world = (log(1))(async)(log(3));
//1
//3
//async process Done!
Run Code Online (Sandbox Code Playgroud)

到目前为止,顺利,现在我们尝试使用绑定

world = (log(1))
  (bind((async), (log(x))));

//should be
//1
//async process Done!
//3

//in fact
//ReferenceError: x is not defined
Run Code Online (Sandbox Code Playgroud)

你可以修改一下吗?

一个关于retrun x, y;多重价值的问题

我不明白

  // seq :: IO a -> IO b -> IO b
    var seq = function(x, y)
    {
      return function()
      {
        return x(), y();
      };
    };
Run Code Online (Sandbox Code Playgroud)

正如图书馆作者所提到的那样

请注意,这在Haskell中是不可能的,因为一个函数不能返回两个结果.而且,在我的拙见中,它看起来很难看.

我同意,不知道这是什么

return x(), y();

多重回报值.

我在谷歌搜索并搜索,但找不到答案.

这是什么??

(以防万一,我会选择这个hack语法)

谢谢!

Aad*_*hah 26

因此,如果我正确理解了这个问题,您希望在JavaScript中链接IO操作.为此,首先需要定义IO操作的内容.想到IO动作的一种方法是它只是一个不带参数的函数.例如:

// log :: a -> IO b

function log(x) {
    return function () {       // IO action
        return console.log(x);
    };
}
Run Code Online (Sandbox Code Playgroud)

将IO操作表示为不带参数的函数的一个优点是它与thunk(未评估的表达式)的表示相同.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
Run Code Online (Sandbox Code Playgroud)

bind在JavaScript中为IO操作编写等效函数很容易:

// bind :: IO a -> (a -> IO b) -> IO b

function bind(x, y) {
    return function () {
        return y(x())();
    };
}
Run Code Online (Sandbox Code Playgroud)

假设你有一个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();
    };
}
Run Code Online (Sandbox Code Playgroud)

这里我们评估IO表达式x,不关心它的结果然后返回IO表达式y.这正是>>运营商在Haskell中的作用.请注意,整个操作包含在多余的懒惰功能中.

Haskell还具有将return值提升为monadic上下文的函数.由于return是JavaScript中的关键字,我们会将其称为unit:

// unit :: a -> IO a

function unit(x) {
    return function () {
        return x;
    };
}
Run Code Online (Sandbox Code Playgroud)

事实证明sequence,Haskell中还有一个运算符,它在列表中对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;
    };
}
Run Code Online (Sandbox Code Playgroud)

这就是我们所需要的一切.现在我们可以写:

var world = sequence([log("1"), log("2"), log("3"), log("4")]);

world();

// 1
// 2
// 3
// 4
Run Code Online (Sandbox Code Playgroud)

希望有所帮助.


是的,确实可以使用您的语法链接IO操作.但是,我们需要重新定义IO操作的内容:

function action(x) {
    return function (y) {
        return y ? action(seq(x, y)) : x();
    };
}
Run Code Online (Sandbox Code Playgroud)

让我们action通过一个例子来理解函数的作用:

// log :: a -> IO b
// log :: a -> IO r -> IO r

function log(x) {
    return action(function () {
        return console.log(x);
    });
}
Run Code Online (Sandbox Code Playgroud)

现在你可以这样做:

log("1")();         // :: b
log("1")(log("2")); // :: IO r
Run Code Online (Sandbox Code Playgroud)

在第一种情况下,我们评估了IO操作log("1").在第二种情况下,我们测序IO动作log("1")log("2").

这允许你这样做:

var world = (log("1"))(log("2"))(log("3"))(log("4"));

world();

// 1
// 2
// 3
// 4
Run Code Online (Sandbox Code Playgroud)

另外你还可以这样做:

var newWorld = (world)(log("5"));

newWorld();

// 1
// 2
// 3
// 4
// 5
Run Code Online (Sandbox Code Playgroud)

等等....

其他一切都是一样的.请注意,这在Haskell中是不可能的,因为一个函数不能返回两个结果.而且,在我的拙见中,它看起来很难看.我更喜欢使用sequence.但是,这就是你想要的.

  • 这是我在stackoverflow中见过的最好的答案之一.你真的很棒.我确认你的代码完美无瑕,并且确实满足了我的所有意图.此外,这个答案对于所有其他研究函数式编程的人来说都是非常有价值的.非常感谢.谢谢!!!! (2认同)

Zir*_*rak 7

让我们来看看这里发生了什么:

var log = function(args)
{
  var f0 = function()
  {
    return console.log(args);
  };

  return function(f1)
  {
    return function()
    {
      f0();
      return f1;
    };
  };
};
Run Code Online (Sandbox Code Playgroud)

并且内联了一下:

var log = function(args) {
  return function(f1) {
    return function() {
      console.log(args);
      return f1;
    };
  };
};
Run Code Online (Sandbox Code Playgroud)

所以我们返回一个f接受函数的函数f1,并返回一个g执行逻辑并返回的函数f1.相当满口!你的问题是为什么

(log('1'))(log('2'))(log('3'));
Run Code Online (Sandbox Code Playgroud)

记录1.我放弃了,log('4')因为去3是足以显示你描述的情况.要回答这个问题,让我们玩编译器并进行内联游戏吧!

(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;
    }
  }
)
Run Code Online (Sandbox Code Playgroud)

简单的替代.我拿了每个实例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;
    }
  }
)
Run Code Online (Sandbox Code Playgroud)

这个有点棘手:我扩展了第一个函数调用.最上面的函数接收了一个f1我们刚刚提供了一个值的参数,所以我进入函数并用f1给定的值(结果log('2'))替换了每个匹配项,就像log参数一样.

如果你仍然不遵循,请再次查看此处发生的事情,但我的建议是自己动手:将代码段复制到您喜欢的代码编辑器中并自行进行扩展.

现在你可能会看到log('1')被调用的原因.我们编译器需要做的下一件事就是处理下一个函数调用.而whadya知道,该功能的第一行是console.log!做得更好!

我们能做什么!?

我不知道Haskell或IO Monad,但按照你目前的计划,我认为你不能用基本功能做你想做的事情,而不是那样.如果你能用这个...呃......模式说出你想解决什么问题,也许我们可以提供帮助!