Ramda连锁用法

时间:2017-08-20 20:36:25

标签: ramda.js

来自documentation

var duplicate = n => [n, n];
R.chain(duplicate, [1, 2, 3]); //=> [1, 1, 2, 2, 3, 3]
R.chain(R.append, R.head)([1, 2, 3]); //=> [1, 2, 3, 1]

第一个例子非常简单,它将duplicate()应用于数组中的每个元素并连接结果。但我无法理解第二个例子。究竟是如何将R.append + R.head映射到数组上的呢?有人可以为第二个例子提供一步一步的解释吗?

我熟悉构图和曲目。

由于

4 个答案:

答案 0 :(得分:12)

第二个示例显示R.chain如何使用除数组之外的其他内容,例如函数(或实现Fantasy Land chain规范的任何内容)。

如果您熟悉映射和连接数组的概念,您可以考虑将函数映射到另一个函数作为普通函数组合。连接部分需要进一步解释。

R.chain声明其签名为:

Chain m => (a → m b) → m a → m b

对于数组,我们可以将m[]交换为:

(a → [b]) → [a] → [b]

对于接收某个参数r的函数,它变为:

(a → r → b) → (r → a) → (r → b)

因此,只有可用的那些类型的知识,产生最终r → b函数的唯一方法是执行以下操作:

  • 将收到的参数r传递给第二个函数以生成a
  • 将新a和原始r同时应用于第一个函数,以生成结果b

或代码:

// specialised to functions
const chain = (firstFn, secondFn) =>
  x => firstFn(secondFn(x), x)

交换示例中的函数,您可以看到它变为:

x => R.append(R.head(x), x)

如果您熟悉R.converge,那么这是有效的:

R.converge(firstFn, [secondFn, R.identity])

答案 1 :(得分:0)

可能更容易首先查看函数的R.chain的抽象版本,并区分被视为monad的函数m: r -> a和被视为Kleisli箭头的函数f: a -> r -> b,如上所述在this answer

然后R.chain被定义为:

// (a -> r -> b, r -> a) -> r -> b
R.chain = (f, m) => x => f(m(x))(x)

x是某种配置参数时,这可能很有用,fm都相同。然后a = m(x)m为该参数返回的值,g = f(_)(x)f为相同参数返回的函数。将x视为同时包含mf的某种环境。然后上面的定义可以分解为:

R.chain = (f, m) => x => {
    const a = m(x)
        , g = a => f(a)(x)
    return g(a)
}

相比之下,函数的R.map对应于f独立于该参数x的情况:

// (a -> b, r -> a) -> r -> b
R.map = (f, m) => x => f(m(x))

当然,这是来自外部的通常功能组合。

在Haskell中定义chain(又名bind)的另一种概念方法是应用map(又名fmap)后跟flatten(aka {{ 1}})。

join

现在,对于R.chain = (f, m) => { // m1: r -> r -> b const m1 = R.map(f, m) // flattening to fm1: r -> b const fm1 = x => m1(x)(x) return fm1 } m = x => R.head(x)f = a => x => R.append(a)(x)相当于将参数R.chain(f, m)放入xf并组成结果:

m

给出了预期的结果。

警告。请注意,此处的x => R.append(R.head(x))(x) 功能必须是curry,因为它代表Kleisli箭头R.append。顺便提一下,a -> r -> b提供了相同的命名功能,也就是未经证实的功能,但它是这里使用的curry。要查看此内容,请让我们自定义Ramda

R.append

然后注意Ramda的REPL如何抛出错误并给出意想不到的结果:

// Define our own uncurried append
const appendCustom = (a, b) => R.append(a, b)
R.chain(appendCustom, R.head)([1, 2]);
// => t(...) is not a function

http://ramdajs.com/repl/?v=0.25.0#?%2F%2F%20Define%20our%20own%20uncurried%20append%0Aconst%20appendCustom%20%3D%20%28a%2C%20b%29%20%3D%3E%20R.append%28a%2C%20b%29%0AR.chain%28appendCustom%2C%20R.head%29%28%5B1%2C%202%5D%29%3B%0A

这里真正发生了什么,const appendCustom = (a, b) => R.append(a, b) 以curry形式执行:appendCustom,第二个调用被委托给一些返回错误的内部函数。

答案 2 :(得分:-1)

希望这有帮助

let R = require('ramda')

// using vanillajs
let append = (arr1) => (arr2) => arr2.concat(arr1)
let double = (arr1) => arr1.map( x => 2*x )

let chain = (f, g) => arr => {
   let yarr = g(arr)
   return f(yarr)(arr)
}

console.log(chain(
   append,
   double
)([10, 15, 20]))

//using Ramda
console.log(R.chain(append, double)([10, 15, 20]))

答案 3 :(得分:-1)

chain(大约)定义为(对于函数):(fn, monad) => x => fn(monad(x))(x)

因此,我们可以像这样转换R.chain(R.append, R.head)([1, 2, 3]);

R.chain(R.append, R.head)([1, 2, 3]);

R.append(R.head([1, 2, 3]), [1, 2, 3]); // This is the important step

R.append(1, [1, 2, 3]);

[1, 2, 3, 1];

The actual source:

var chain = _curry2(_dispatchable(['fantasy-land/chain', 'chain'], _xchain, function chain(fn, monad) {
  if (typeof monad === 'function') {
    return function(x) { return fn(monad(x))(x); };
  }
  return _makeFlat(false)(map(fn, monad));
}));

其中重要的部分是:

function chain(fn, monad) {
  if (typeof monad === 'function') {
    return function(x) { return fn(monad(x))(x); };
  }
  return _makeFlat(false)(map(fn, monad));
}

根据the comment in the code,“_makeFlat是一个辅助函数,它根据传入的标志返回一个或一个完全递归的函数。”

_makeFlat(false)似乎等同于unnest_makeFlat(true)似乎等同于flatten