首页 > 解决方案 > 你将如何重写这个多参数函数,最终实现无点风格?

问题描述

我是 Ramda 和函数式编程的新手,我想知道有人会如何改进下面的代码或将其转换为无点样式

const doc = {
  passwordRecovery: {
    requested: true,
    expiresAt: new Date(Date.now() + 1000).toISOString(),
    code: 'abc'
  }
}

const req = {
  password: '123',
  passwordRecovery: {
    code: 'abc'
  }
}

const pathCode = R.path(['passwordRecovery', 'code'])
const isValidCode = R.curry(
    (doc, req) => R.all(
        R.hasPath(['passwordRecovery', 'code'], req),
        R.pathEq(['passwordRecovery', 'requested'], true, doc),
        R.compose(R.complement(R.isNil), pathCode)(doc),
        R.equals(pathCode(req), pathCode(doc)),
        R.pipe(
            R.path(['passwordRecovery', 'expiresAt']),
            Date.parse,
            R.gte(R.__, Date.now()),
        )(doc)
    )
);

isValidCode(doc)(req)

标签: javascriptfunctional-programmingramda.js

解决方案


几个建议:

避免混淆

不熟悉柯里化的人经常会说同样的话:add(1)(2)当你可以做的时候为什么要做add(1, 2)?他们是对的。这个例子根本不适合咖喱。

如果您的团队刚刚熟悉函数式编程,请不要让他们感到不必要的困惑。如果您可以一次性提供所有参数,请执行以下操作:

isValidCode(doc, req); // not isValidCode(doc)(req);

不要为了使用 Ramda 而使用 Ramda

这:R.equals(pathCode(req), pathCode(doc))与: 相同pathCode(req) === pathCode(doc)

如果您确实想使用 Ramda,请考虑eqBy

eqBy(pathCode, req, code);

Pointfree 不是唯一的方法

这当然是一种有趣的编写函数的方式,但它不能成为目标。假设您需要检查a等于'foo'b等于'bar'

这是无点函数:

const fn = useWith(and, [equals('foo'), equals('bar')]);

比这更好:

const fn = (a, b) => a === 'foo' && b === 'bar';

?

是否all正确调用?

根据文档,all需要一个函数和一个列表:

all(x => x === 42)([41, 42, 43]);

除非我弄错了,否则您所做的是all使用每个函数调用的结果进行调用。例如

all(true, false, true, ...);

避免__占位符

gte(__, Date.now())可以改为flip(gte)(Date.now())

始终如一

在一种情况下,您允许将路径设置为undefined

// true even for `{passwordRecovery: {code: undefined}}`
hasPath(['passwordRecovery', 'code'], req);

而在另一个你没有:

compose(R.complement(R.isNil), pathCode)(doc)

保持一致将允许这样做:

const notNil = complement(isNil);
both(pathSatisfies(notNil, pathCode), req, doc)

推荐阅读