组合不纯函数

Composing impure functions

假设我有以下不纯函数:

// Mounts a foo instance into the given dom node
// (this is an implementation detail of the Foo library),
// then returns the foo instance.
const createFoo = (FooConstructor, domNode, options) => {
  return new FooConstructor(domNode, options);
};

const outlineFoo = foo => {
  foo.getCanvas().outline = true;
  return foo;
}

如果我想使用 R.compose 创建 Foo 实例并突出显示它,我可以编写一个函数来执行以下操作:

const createFooWithHighlights = (FooConstructor, domNode, options) => {
  return R.compose(
    outlineFoo,
    createFoo
  )(FooConstructor, domNode, options)
}

如果我想使用命令式编程来完成同样的事情,我会这样做:

const createFooWithHighlights = (FooConstructor, domNode, options) => {
  const foo = createFoo(FooConstructor, domNode, options);
  outlineFoo(foo);
  return foo;
}

无论我们选择哪个,它都会被相同地调用:

const highlightedFooInDOM = createFooWithHighlights(Foo, document.body, {})

既然所有这些功能都会产生副作用,我应该避免使用 R.compose 吗?是否有关于纯度和功能组成的规则?

首先,一个简化。你应该能够更简单地写 createFooWithHighlights 为:

const createFooWithHighlights = R.compose(outlineFoo, createFoo);

现在开始真正的问题:

Since all of these functions produce side effects, should I avoid using R.compose? Are there rules governing purity and function composition?

我会说不是。函数式编程当然是关于减少副作用,将它们分流到程序的边缘,甚至可能将它们封装在模式和结构中(请参阅对 IO Monad 的评论,这当然是个好建议,但可能为时过早。)

但这与消除副作用无关。一个绝对没有副作用的程序只能计算一些硬编码输入的结果......然后懒得与你分享。

Ramda(免责声明:我是作者之一)严格避免副作用。但这是应该的。它是一个功能实用程序库;你不是那些决定你的程序如何与世界其他地方互动的人之一。或者至少我们 Ramda 团队没有。所以 Ramda 函数是纯函数。此外,Ramda 旨在让您在实用的情况下以纯函数的方式轻松工作。

但这并不意味着它的函数应该只能用于创建其他纯函数。当你需要创建副作用时,引入 Ramda 的工具完全没有问题,尤其是像功能组合这样重要的工具。

所以你想做的似乎完全符合 Ramda 的正常使用。