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 map
ped over with a function, the way an array can be map
ped 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.
[1,2,3].map(fnSquare).reduce(fnSum)
2. largely academic "look ma no var" Y-combinator-esque structures. 3. usingFunction.prototype
to modify the behavior of other functions, likevar isMissingID=fnContains.partial("id").negate();
– ImmitigableuseWith
and/orconverge
, they probably wouldn't change too much in the use of Ramda except that it makes points-free solutions that Ramda can enable little more attractive than the more pointed ones. – Fluidextract