什么是我的应用函子没有使用Ramda的ap?

时间:2017-10-15 05:31:25

标签: javascript functional-programming ramda.js applicative

我正在尝试学习如何在javascript中使用applicative functor并遇到ap方法。我试图用它来组合三个数组:

const products = ['teeshirt', 'sweater']
const options = ['large', 'medium', 'small']
const colors = ['red', 'black']

按照documentation我正在尝试这个:

const buildMerch = product => option => color =>`${product}-${option}-${color}`

const merchandise = R.ap([buildMerch], [products, options, colors])

但这给了我三个功能:

[function (option) {
  return function (color) {
    return product + '-' + option + '-' + color;
  };
}, function (option) {
  return function (color) {
    return product + '-' + option + '-' + color;
  };
}, function (option) {
  return function (color) {
    return product + '-' + option + '-' + color;
  };
}]

...而不是我期望的数组的组合结果:

["teeshirt- large-red", "teeshirt- large-black", "teeshirt- medium-red", "teeshirt- medium-black", "teeshirt- small-red", "teeshirt- small-black", "sweater- large-red", "sweater- large-black", "sweater- medium-red", "sweater- medium-black", "sweater- small-red", "sweater- small-black"]

我做错了什么?我该如何解决这个问题?

这是一个问题的jsbin:https://jsbin.com/fohuriy/14/edit?js,console

3 个答案:

答案 0 :(得分:3)

根据文档,

ap将函数列表应用于值列表。您的函数buildMerch具有以下“类型”:

buildMerch :: String -> String -> String -> String

最简单的apmap:对于任何应用仿函数,我们都会得到:

pure f <*> a
  ======
map f a

对于数组,purex => [x]。所以,

R.ap([buildMerch], [foo, bar, baz])
  ======
R.map(buildMerch, [foo, bar, baz])

通过在参数列表上映射buildMerch,我们将其部分应用于相关数组。做你想要的表达是:

const merchandise = R.ap(R.ap(R.map(buildMerch, products), options), colors);

首先,我们在产品数组上映射buildMerch。这为我们提供了一系列带有两个参数的函数:[String -> String -> String]。然后,我们使用R.ap将其与options :: [String]结合起来,options[String -> String]数组中的每个参数应用于第一个数组中的每个函数。现在我们有R.ap,最后我们colors使用 foreach (Control ctrl in this.Controls) { if (ctrl is Panel) { (ctrl as Panel).BackColor = Color.FromArgb(214, 219, 233); } if (ctrl is GroupBox) { (ctrl as GroupBox).BackColor = Color.FromArgb(214, 219, 233); } } 来获取您想要的最终字符串数组。

答案 1 :(得分:1)

AP将函数列表应用于值列表。在您的情况下,您将使用指定数组中的每个元素调用buildMerchproducts,然后调用options,之后调用colors,而不是调用每个元素你的阵列。这与您的方法签名不匹配,您需要三个参数。

答案 2 :(得分:1)

解决此问题的另一种方法是将ap添加到本机javascript数组中。通过这种方式,您实际上将Array转换为应用程序仿函数,您不需要库,并且它可以使用与您可能想要使用的任何其他应用程序仿函数相同的界面。

// add ap
Array.prototype.ap = function(anotherArray) {
  return this.map(el =>
    anotherArray.map(el)
  ).flatten();
};

这取决于展平(或“加入&#39;”)。

// add flatten
Array.prototype.flatten = function() {
  let results = [];
  this.forEach(subArray => {
    subArray.forEach(item => {
      results.push(item);
    });
  });
  return results;
};

现在:

const products = ['teeshirt', 'sweater'];
const options = ['large', 'medium', 'small'];
const colors = ['red', 'black'];
const buildMerch = product => option => color =>`${product}-${option}-${color}`;

const merchandise = products.map(buildMerch).ap(options).ap(colors);

现在你也可以解除所有三个:

const liftA3 = (fn, functor1, functor2, functor3) =>
  functor1.map(fn).ap(functor2).ap(functor3);

liftA3(buildMerch, products, options, colors) // also returns merchandise