带有隐式空值的 Ramda Curry

Ramda Curry with Implicit Null

我一直在尝试学习 Ramda 库并开始研究函数式编程。这主要是学术性的,但我试图创建一个很好的日志记录功能,我可以使用它从 pipecompose

中将值记录到控制台

我注意到的事情

一旦你用 Ramda 柯里化了一个函数,调用一个没有任何参数的函数 returns 相同的函数

f() returns f

但是

f(undefined)f(null)

不要。


我创建了一个实用程序函数来对齐这些调用,这样

f() 等于 f(null) 即使 f 是柯里化的。

// Returns true if x is a function
const isFunction = x => 
  Object.prototype.toString.call(x) == '[object Function]';

// Converts a curried fuction so that it always takes at least one argument
const neverZeroArgs = fn => (...args) => {
  let ret = args.length > 0 ?
    fn(...args) :
    fn(null)

  return isFunction(ret) ?
    neverZeroArgs(ret) :
    ret
}

const minNullCurry = compose(neverZeroArgs, curry);

正在使用中:

const logMsg = minNullCurry((msg, val) => {
  if(isNil(msg) || msg.length < 1) console.log(val);
  else if(isNil(val)) console.log(msg);
  else console.log(`${msg}: `, val);
});

const logWithoutMsg = logMsg();
logWithoutMsg({Arrr: "Matey"})

然后如果我想在 Ramda 管道或组合中使用它,我可以这样做:

// Same as logMsg, but always return the value you're given
const tapLog = pipe(logMsg, tap);

pipe(
  prop('length'),
  tapLog() // -> "5"
)([1,2,3,4,5]);

pipe(
  prop('length'),
  tapLog('I have an thing of length') // -> "I have an thing of length: 5"
)([1,2,3,4,5]);

pipe(
  always(null),
  tapLog('test') // -> "test"
)([1,2,3,4,5]);

我刚开始使用 Ramda,想知道它是否带有任何可能使它变得有点的东西 easier/cleaner。我确实意识到我可以这样做:

const logMsg = msg => val => {
  if(isNil(msg)) console.log(val);
  else if(isNil(val)) console.log(msg);
  else console.log(`${msg}: `, val);
});

我已经完成了,但现在我必须永远一次应用每个参数 1。

这很好,但我来这里是为了了解是否有任何有趣的替代方案。我怎样才能转换一个咖喱函数,以便 f() returns f(null) 或者它是一种代码味道,甚至想要这样做?

(这里是 Ramda 的创始人和维护者)。

Once you've curried a function with Ramda, invoking a function without any parameters returns the same function

f() returns f

but

f(undefined) and f(null)

do not.

没错。这是设计使然。在 Ramda 中,对于 i < n,其中 n 是函数长度,使用 i 参数调用函数,然后使用 j 参数调用函数应该具有相同的行为,就好像我们最初用 i + j 个参数调用它。 i 为零也不例外。多年来,对此一直存在一些争议。另一位联合创始人不同意我的看法,但我们的第三位合作者同意我的看法,从那以后一直如此。请注意,另一位创始人不想将其视为您提供了 undefined/null,而是抛出错误。一致性有很多话要说。

I'm here to learn if there are any fun alternatives. How can I transform a curried function so that f() returns f(null) or is it a code smell to even want to do that?

这不是代码味道,根本不是。 Ramda 不会为您提供这个,而且可能永远不会,因为它与库的其余部分并不匹配。 Ramda 需要能够区分空调用和空输入,因为对于某些用户来说这可能很重要。但是从来没有人说过您的所有合成工具都必须来自特定的库。

我认为你所做的没有任何问题。

如果您对不同的 API 感兴趣,像这样的东西可能会很有趣:

const {pipe, prop, always} = R

const tapLog = Object .assign (
  (...val) => console .log (...val) || val,
  {
    msg: (msg) => (...val) => console .log (`${msg}:`, ...val) || val,
    val: (...val) => (_) => console .log (...val) || _
  }
)

tapLog ({Arrr: "Matey"})

pipe(
  prop('length'),
  tapLog // -> "5"
)([1,2,3,4,5]);

pipe(
  prop('length'),
  tapLog.msg('I have an thing of length') // -> "I have an thing of length: 5"
)([1,2,3,4,5]);

pipe(
  always(null),
  tapLog.val('test') // -> "test"
)([1,2,3,4,5]);
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.27.1/ramda.min.js"></script>