如何在JavaScript中使用pointfree样式而不会失去可读性?

Jer*_*rry 7 javascript functional-programming pointfree ramda.js

当我尝试以无点样式编写JavaScript时,我发现如果你强制使用这种风格的每个函数,你有时会失去它的可读性.例如:

import R from 'ramda'

const ceil = Math.ceil

const pagination = {
  total: 101,
  itemsPerPage: 10,
  currentPage: 1
}

// ================= Pointful style ==================
const pageCount = (pagination) => {
  const pages = ceil(pagination.total / pagination.itemsPerPage)
  const remainPages = pagination.total % pagination.itemsPerPage === 0 ? 0 : 1
  return pages + remainPages
} 

pageCount(pagination) // => 11

// ================ Pointfree style ==================
const getPages = R.pipe(
  R.converge(R.divide, [R.prop('total'), R.prop('itemsPerPage')]),
  ceil
)

const getRemainPages = R.ifElse(
  R.pipe(
     R.converge(R.modulo, [R.prop('total'), R.prop('itemsPerPage')]),
     R.equals(0)
  ),
  R.always(0),
  R.always(1)
)

const pageCount2 = R.converge(R.add, [
  getPages,
  getRemainPages
])

pageCount2(pagination) // => 11
Run Code Online (Sandbox Code Playgroud)

我写了一个简单的分页模块,用于计算pageCount以有点样式和无点样式给出总项目数和每页项目数.显然,有点样式比无点样式版本更具可读性.后者有点模糊.

我做得对吗?有没有办法让dotfree样式的代码更具可读性?

小智 8

手工构图

让我们从手动编写功能开始:

const calcPages = (totalItems, itemsPerPage) =>
 ceil(div(totalItems, itemsPerPage));


const div = (x, y) => x / y;

const ceil = Math.ceil;


const pagination = {
  total: 101,
  itemsPerPage: 10,
  currentPage: 1
}


console.log(
  calcPages(pagination.total, pagination.itemsPerPage)
);
Run Code Online (Sandbox Code Playgroud)

程序化组成

下一步我们将参数抽象出来:

const comp2 = (f, g) => (x, y) => f(g(x, y));

const div = (x, y) => x / y;

const ceil = Math.ceil;


const calcPages = comp2(ceil, div);


const pagination = {
  total: 101,
  itemsPerPage: 10,
  currentPage: 1
}


console.log(
  calcPages(pagination.total, pagination.itemsPerPage)
);
Run Code Online (Sandbox Code Playgroud)

函数定义现在是无点的.但是调用代码不是.如果您知道高阶函数的comp2工作原理,那么表达式comp2(ceil, div)对您来说非常具有说明性.

现在很明显,这calcPages是错误的名称,因为功能组成更加通用.我们称之为...... intDiv(好吧,可能有一个更好的名字,但我很擅长数学).

解构修饰符

在下一步中,我们进行修改,intDiv以便它可以处理对象:

const destruct2 = (x, y) => f => ({[x]:a, [y]:b}) => f(a, b);

const comp2 = (f, g) => (x, y) => f(g(x, y));

const div = (x, y) => x / y;

const ceil = Math.ceil;


const intDiv = comp2(ceil, div);

const calcPages = destruct2("total", "itemsPerPage") (intDiv);


const pagination = {
  total: 101,
  itemsPerPage: 10,
  currentPage: 1
}


console.log(
  calcPages(pagination)
);
Run Code Online (Sandbox Code Playgroud)

calcPages再次调用了修改后的函数,因为它现在需要一个特定的pagination对象,因此不太通用.

如果你知道所涉及的高阶函数是如何工作的,那么一切都是声明性的并且可读性很好,即使它是以无点样式编写的.

结论

无点样式是功能组合,currying和高阶函数的结果.这本身并不是一件事.如果您停止使用这些工具以避免无点样式,那么您将失去函数式编程提供的大量表现力和优雅.


dav*_*ers 4

让我们从一个简单的例子开始:

//    inc :: Number -> Number
const inc = R.add(1);
Run Code Online (Sandbox Code Playgroud)

我发现上面的内容比它的“有意义的”等价物更清楚:

//    inc :: Number -> Number
const inc = n => R.add(1)(n);
Run Code Online (Sandbox Code Playgroud)

一旦人们熟悉了部分应用 Ramda 函数,上面一行中的 lambda 就只是噪音。

让我们走向另一个极端:

//    y :: Number
const y = R.ifElse(R.lt(R.__, 0), R.always(0), Math.sqrt)(x);
Run Code Online (Sandbox Code Playgroud)

用“有意义的”风格写得更清楚

//    y :: Number
const y = x < 0 ? 0 : Math.sqrt(x);
Run Code Online (Sandbox Code Playgroud)

我的建议是在简单的情况下使用无点表达式,并在表达式变得复杂时恢复到“有点”样式。我经常走得太远,然后撤消最后几次更改以恢复更清晰的表达。