带有隐式空值的 Ramda Curry
Ramda Curry with Implicit Null
我一直在尝试学习 Ramda 库并开始研究函数式编程。这主要是学术性的,但我试图创建一个很好的日志记录功能,我可以使用它从 pipe
或 compose
中将值记录到控制台
我注意到的事情
一旦你用 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>
我一直在尝试学习 Ramda 库并开始研究函数式编程。这主要是学术性的,但我试图创建一个很好的日志记录功能,我可以使用它从 pipe
或 compose
我注意到的事情
一旦你用 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()
returnsf
but
f(undefined)
andf(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>