97

I'm learning javascript FP by reading DrBoolean's book.

I searched around for functional programming library. I found Ramda and Folktale. Both claim to be functional programming library.

But they are so different:

  • Ramda seems to contain utility functions for dealing with list: map, reduce, filter and pure functions: curry, compose. It doesn't contain anything to deal with monad, functor.

  • Folktale however doesn't contain any utility for list or functions. It seems to implement the some algebraic structures in javascript like monad: Maybe, Task...

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

Can these very different libraries both be called functional, and if so, what makes each one a functional library?

PhD
  • 10,410
  • 12
  • 56
  • 102
Aaron Shen
  • 7,354
  • 8
  • 40
  • 76
  • 1
    use what fits your needs and style and is well documented – charlietfl Oct 08 '15 at 23:05
  • 1
    i've found there's really three commonly-intended meanings for "functional programming", especially in JS. 1. using higher-order pure functions on sets like arrays., ex `[1,2,3].map(fnSquare).reduce(fnSum)` 2. largely academic "look ma no _var_" Y-combinator-esque structures. 3. using `Function.prototype` to modify the behavior of other functions, like `var isMissingID=fnContains.partial("id").negate();` – dandavis Oct 08 '15 at 23:05
  • 11
    A Ramda author here: Ramda is a fairly low-level utility library. It is intended to make a certain functional style simpler in JS, especially working by composing functions. Ramda works well with the FantasyLand specification, including implementations of it such as Folktale. Those libraries are designed for a somewhat different purpose. They are built around recognizing common abstract data types and allowing consistent access to them: such things as Monoids, Functors, and Monads. Ramda will work with them, and has a side project to create some, but it is, as you say, a very different focus. – Scott Sauyet Oct 09 '15 at 01:53
  • @dandavis: There's a community trying to do substantially more with FP in JS than is encompassed by your three meanings. And the OP seems to have run across them in both the Ramda and the FantasyLand worlds. – Scott Sauyet Oct 09 '15 at 02:03
  • @ScottSauyet: i think the big schism is "mere functional iteration" and "real FP w/composition". i'd not played with ramda until now. It seems to be like #3, harking all the way back to Prototype.js, but with a larger stock lib and an eye towards composition. I think it's focused on "modifying the behavior of other functions", but there's a lot of good stuff in there (__, converge, invoker, etc), maybe too much. a lot of the simple utils (especially List ones) can probably be outmoded by simple fat arrows these days (less for the coder to remember)... anyway, good work. – dandavis Oct 09 '15 at 03:11
  • @dandavis: Thanks. Added an answer after this was reopened, explaining in more detail some of the differences between existing libraries. While fat arrows make a lot of things simpler and may reduce the need for `useWith` and/or `converge`, 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. – Scott Sauyet Oct 14 '15 at 17:15
  • @ScottSauyet is there a discussion forum or something for Ramda anywhere? – Keith Nicholas Oct 23 '15 at 07:53
  • 1
    @KeithNicholas: Yes, there's a [Gitter Room](https://gitter.im/ramda/ramda) – Scott Sauyet Oct 23 '15 at 18:05
  • 2
    Take a look at Sanctuary too - https://github.com/plaid/sanctuary This is based on ramda but also covers fantasy-land types. – arcseldon Feb 19 '16 at 15:42

1 Answers1

182

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 it's 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.

Scott Sauyet
  • 37,179
  • 4
  • 36
  • 82
  • 1
    Would it be fair to say ES6 has the capability for lazy evaluation with the introduction of Yield? https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield – Marcel Lamothe Dec 21 '15 at 18:42
  • 8
    No. `yield` makes it easier to do the sorts of lazy list processing done by `Lazy` or `lz.js`. But it doesn't help with language level laziness. `someFunc(a + b)` in JS first adds the values of `a` and `b` then supplies that result as the parameter to someFunc. The equivalent in Haskell doesn't do that. If the function called never uses that value, it never performs the addition. If it does eventually use it, it's considered an expression to be calculated until its result is needed. If you never do anything that will force it (like IO) it will never actually perform the calculation. – Scott Sauyet Dec 22 '15 at 02:06
  • @ScottSauyet perhaps you might argue "lazy evaluation" in some form is available via ES6 generators for instance - alot of JS frameworks have "laziness" characteristics - RxJs, ImmutableJs etc. – arcseldon Feb 19 '16 at 15:30
  • Point taken if comparing to Haskell's language level laziness. – arcseldon Feb 19 '16 at 15:40
  • 9
    This answer should be a chapter or section in DrBoolean's book. :) – Seth Dec 12 '16 at 19:15
  • @ScottSauyet Plese explain what do you mean by "Ramda operates especially on lists, but also on objects, and sometimes on Strings."? It is not let say, Elixir, no lists here. Perhaps you mean arrays? But then in JavaScript, there is no "true" array - they are objects. – Wojciech Bednarski Nov 07 '18 at 18:03
  • 1
    Ramda documentation tends to use "list" as a shorthand for the closest thing JS offers to lists, dense arrays. These have different performance characteristics than pure lists, and of course a somewhat different API, but they can be used to for much the same purposes, and they are the closest native (but see https://github.com/funkia/list) type available to Ramda's API, which conceptually wants to work with pure lists. I would argue the not-true arrays point, as C-style arrays are no more canonical than JS ones, and none really close to mathematical ones, but that's a minor point. – Scott Sauyet Nov 08 '18 at 13:48
  • @MarcelLamothe closes to lazy evaluation you would get if you put everything into anonymouse functions to be called when necessary. So `someFunc(() => a+b)` and even `() => someFunc(() => a+b)` and even more with: `() => someFunc(() => (() => a) + (() => b))`. Lazy everything! Very useful if e.g. `a` is very expensive and you can avoid computing it. – przemo_li Sep 11 '20 at 10:33