为什么民间故事和兰达如此不同?

我通过阅读 DrBoolean 的 来学习 javascript FP。

我到处搜索函数式编程库,我找到了 Ramda 和 Folkstory,它们都声称自己是函数式编程库。

但它们如此不同:

  • Ramda 似乎包含用于处理 list 的实用函数: map、 reduce、 filter 和纯函数: curry、 compose。它不包含任何单子,函数。

  • 但是 Folkstory 不包含任何用于列表或函数的实用程序。它似乎在 javascript 中实现了一些代数结构,如 monad: Maybe,Task..。

实际上我发现了更多的图书馆,它们似乎都属于这两类。下划线和加长线就像 Ramda。幻想世界,无点幻想就像民间故事。

这些非常不同的库是否都可以称为 功能性的,如果可以,那么是什么使得每个库都成为函数库呢?

12634 次浏览

Functional Features

There is no clear-cut boundary of what defines functional programming or a functional library. Some features of functional languages are built into Javascript:

  • First-class, higher-order functions
  • Lambdas/Anonymous functions, with closures

Others are possible to accomplish in Javascript with some care:

  • Immutability
  • Referential Transparency

Still others are part of ES6, and partially or fully available right now:

  • Compact, even terse, functions
  • Performant recursion through tail-call optimization

And there are plenty of others which are really beyond the normal reach of Javascript:

  • Pattern matching
  • Lazy evaluation
  • Homoiconicity

A library, then, can pick and choose what sorts of features it's trying to support and still reasonably be called "functional".

Fantasy-land specification

Fantasy-land is a specification for a number of the standard types ported from mathematical Category Theory and Abstract Algebra to functional programming, types such as Monoid, Functor, and Monad. These types are fairly abstract, and extend possibly more familiar notions. Functors, for instance, are containers which can be mapped over with a function, the way an array can be mapped over using Array.prototype.map.

Folktale

Folktale is a collection of types implementing various parts of the Fantasy-land specification and a small collection of companion utility functions. These types are things like Maybe, Either, Task (very similar to what is elsewhere called a Future, and a more lawful cousin to a Promise), and Validation

Folktale is perhaps the best-known implementation of the Fantasy-land specification, and it is well-respected. But there is no such thing as a definitive or default implementation; fantasy-land only specifies abstract types, and an implementation of course must create such concrete types. Folktale's claim to being a functional library is clear: it provides data types found typically in functional programming languages, ones which make it substantially easier to program in a functional manner.

This example, from the Folktale documentation (note: not in recent versions of the docs), shows how it might be used:

// We load the library by "require"-ing it
var Maybe = require('data.maybe')


// Returns Maybe.Just(x) if some `x` passes the predicate test
// Otherwise returns Maybe.Nothing()
function find(predicate, xs) {
return xs.reduce(function(result, x) {
return result.orElse(function() {
return predicate(x)?    Maybe.Just(x)
:      /* otherwise */  Maybe.Nothing()
})
}, Maybe.Nothing())
}


var numbers = [1, 2, 3, 4, 5]


var anyGreaterThan2 = find(function(a) { return a > 2 }, numbers)
// => Maybe.Just(3)


var anyGreaterThan8 = find(function(a) { return a > 8 }, numbers)
// => Maybe.Nothing

Ramda

Ramda (disclaimer: I'm one of the authors) is a very different type of library. It does not provide new types for you.1 Instead, it provides functions to make it easier to operate on existing types. It is built around the notions of composing smaller functions into larger ones, of working with immutable data, of avoiding side-effects.

Ramda operates especially on lists, but also on objects, and sometimes on Strings. It also delegates many of its calls in such a manner that it will interoperate with Folktale or other Fantasy-land implementations. For instance, Ramda's map function, operates similarly to the one on Array.prototype, so R.map(square, [1, 2, 3, 4]); //=> [1, 4, 9, 16]. But because Folktale's Maybe implements the Fantasy-land Functor spec, which also specifies map, you can also use Ramda's map with it:

R.map(square, Maybe.Just(5)); //=> Maybe.Just(25);
R.map(square, Maybe.Nothing); //=> Maybe.Nothing

Ramda's claims to being a functional library lie in making it easy to compose functions, never mutating your data, and presenting only pure functions. Typical usage of Ramda would be to build up more complex function by composing smaller ones, as seen in an article on the philosphy of Ramda

// :: [Comment] -> [Number]
var userRatingForComments = R.pipe(
R.pluck('username')      // [Comment] -> [String]
R.map(R.propOf(users)),  // [String] -> [User]
R.pluck('rating'),       // [User] -> [Number]
);

Other Libraries

Actually I found more libraries, they all seem fall into the two categorys. underscore, lodash are very like Ramda. Fantasy-land, pointfree-fantasy are like folktale.

That's not really accurate. First of all, Fantasy-land is simply a specification that libraries can decide to implement for various types. Folktale is one of many implementations of that specification, probably the best-rounded one, certainly one of the most mature. Pointfree-fantasy and ramda-fantasy are others, and there are many more.

Underscore and lodash are superficially like Ramda in that they are grab-bag libraries, providing a great number of functions with much less cohesion than something like Folktale. And even the specific functionality often overlaps with Ramda's. But at a deeper level, Ramda has very different concerns from those libraries. Ramda's closest cousins are probably libraries like FKit, Fnuc, and Wu.js.

Bilby is in a category of its own, providing both a number of tools such as the ones provided by Ramda and some types consistent with Fantasy-land. (The author of Bilby is the original author of Fantasy-land as well.)

Your Call

All of these libraries have right to be called functional, although they vary greatly in functional approach and degree of functional commitment.

Some of these libraries actually work well together. Ramda should work well with Folktale or other Fantasy-land implementations. Since their concerns barely overlap, they really don't conflict, but Ramda does just enough to make the interoperation relatively smooth. This is probably less true for some of the other combinations you could choose, but ES6's simpler function syntax may also take some of the pain out of integrating.

The choice of library, or even style of library to use, is going to depend on your project and your preferences. There are lots of good options available, and the numbers are growing, and many of them are improving greatly. It's a good time to be doing functional programming in JS.


1Well, there is a side-project, ramda-fantasy doing something similar to what Folktale does, but it's not part of the core library.