JavaScript链接的函数组成 [英] JavaScript function composition by chaining

查看:112
本文介绍了JavaScript链接的函数组成的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经检查了重复问题的可能性,
并且无法找到确切的解决方案。

I have checked the possibility of duplicate question, and cannot find the exact solution.

我在JavaScript中编写了一些函数链代码,如下所示,工作正常。

I wrote some function chain code in JavaScript as below, and works fine.

var log = function(args)
{
  console.log(args)

  return function(f)
  {
    return f;
  };
};

(log('1'))(log('2'))(log('3'))(log('4'));

//1
//2
//3
//4

我想做这个懒惰的评价。

I want to make this lazy evaluation.

或者撰写函数。

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

出了点问题。
你能解决吗?

Something is very wrong. Can you fix it?

谢谢。

我们有

// 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();
    }
  }
});

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

We also often want async chain reactions badly.

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

So far nice and smooth, now we try to use bind

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

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

//in fact
//ReferenceError: x is not defined

你能修改一下吗?

我不明白

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

,因为图书馆作者提到


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

Note that this is not possible in Haskell because one function can't return two results. Also, in my humble opinion, it looks ugly.

我同意,并且不知道这是什么

I agree, and don't know what this

返回x(),y();

多个返回值。

我在这里搜索并搜索,但找不到答案。

I googled and searched here, but could not find an answer.

这是什么?

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

(just in case, I will chose this hack for the syntax)

谢谢!

推荐答案

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

So if I understand the question correctly, you want to chain IO actions in JavaScript. To do so, you first need to define what an IO action is. One way to think of an IO action is that it is simply a function which takes no arguments. For example:

// log :: a -> IO b

function log(x) {
    return function () {       // IO action
        return console.log(x);
    };
}

将IO操作表示为没有参数的函数的一个优点是它与 thunks 相同(未评估的表达方式)。 Thunk是能够在像Haskell这样的语言中进行延迟评估的东西。因此,你免费得到懒惰。

One advantage of representing an IO action as a function with no arguments is that it is the same representation for thunks (unevaluated expressions). Thunks are the things that enable lazy evaluation in languages like Haskell. Hence you get laziness for free.

现在组成。你如何在JavaScript中组成两个IO动作?在Haskell中,您使用>> 运算符对IO操作进行排序,这通常以>> = (又名 bind )如下:

Now composition. How do you compose two IO actions in JavaScript? In Haskell, you use the >> operator to sequence IO actions, which is usually defined in terms of >>= (a.k.a. bind) as follows:

(>>=) :: Monad m => m a -> (a -> m b) -> m b

(>>) :: Monad m => m a -> m b -> m b
x >> y = x >>= \_ -> y

很容易写出等价的 bind 我们在JavaScript中执行IO操作的功能:

It is easy to write an equivalent bind function for our IO actions in JavaScript:

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

function bind(x, y) {
    return function () {
        return y(x())();
    };
}

假设你有一个IO动作 x :: IO一个。因为它只是一个没有参数的函数,所以当你调用它时它等同于评估IO动作。因此 x():: a 。将此结果提供给函数 y :: a - > IO b 导致IO操作 y(x()):: IO b 。请注意,整个操作都包含在一个多余的懒惰函数中。

Suppose you have an IO action x :: IO a. Since it's just a function with no arguments, when you call it it's equivalent to evaluating the IO action. Hence x() :: a. Feeding this result to the function y :: a -> IO b results in the IO action y(x()) :: IO b. Note that the entire operation is wrapped in a superfluous function for laziness.

同样,实现>> <同样简单/ code>运算符。我们称之为 seq ,如同在“ sequence”。

Similarly, it is just as straightforward to implement the >> operator. Let's call it seq as in “sequence”.

// seq :: IO a -> IO b -> IO b

function seq(x, y) {
    return function () {
        return x(), y();
    };
}

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

Here we evaluate the IO expression x, don't care about its result and then return the IO expression y. This is exactly what the >> operator does in Haskell. Note that the entire operation is wrapped in a superfluous function for laziness.

Haskell还有一个 return 函数,它将值提升为monadic上下文。由于 return 是JavaScript中的关键字,我们将其称为 unit

Haskell also has a return function which lifts a value into a monadic context. Since return is a keyword in JavaScript, we'll call it unit instead:

// unit :: a -> IO a

function unit(x) {
    return function () {
        return x;
    };
}

事实证明还有一个序列运算符,它对列表中的monadic值进行排序。它可以在JavaScript中实现,用于IO操作,如下所示:

As it turns out there's also a sequence operator in Haskell which sequences monadic values in a list. It can be implemented in JavaScript for IO actions as follows:

// 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;
    };
}

这就是我们所需要的。现在我们可以写:

That's all we need. Now we can write:

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

world();

// 1
// 2
// 3
// 4

希望有所帮助。

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

Yes, it is indeed possible to chain IO actions using your syntax. However, we'll need to redefine what an IO action is:

function action(x) {
    return function (y) {
        return y ? action(seq(x, y)) : x();
    };
}

让我们了解行动函数确实使用了一个例子:

Let's understand what the action function does using an example:

// 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操作日志( 1)。在第二种情况下,我们对IO动作进行了排序 log(1) log(2)

In the first case we evaluated the IO action log("1"). In the second case we sequenced the IO actions 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 。但是,这就是你想要的。

Everything else remains the same. Note that this is not possible in Haskell because one function can't return two results. Also, in my humble opinion, it looks ugly. I prefer using sequence instead. However, this is what you wanted.

这篇关于JavaScript链接的函数组成的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆