why are folktale and ramda so different?

12,415

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.

Share:
12,415

Related videos on Youtube

Aaron Shen
Author by

Aaron Shen

Updated on July 14, 2022

Comments

  • Aaron Shen
    Aaron Shen almost 2 years

    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?

    • charlietfl
      charlietfl over 8 years
      use what fits your needs and style and is well documented
    • dandavis
      dandavis over 8 years
      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();
    • Scott Sauyet
      Scott Sauyet over 8 years
      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
      Scott Sauyet over 8 years
      @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.
    • dandavis
      dandavis over 8 years
      @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.
    • Scott Sauyet
      Scott Sauyet over 8 years
      @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.
    • Keith Nicholas
      Keith Nicholas over 8 years
      @ScottSauyet is there a discussion forum or something for Ramda anywhere?
    • Scott Sauyet
      Scott Sauyet over 8 years
      @KeithNicholas: Yes, there's a Gitter Room
    • arcseldon
      arcseldon about 8 years
      Take a look at Sanctuary too - github.com/plaid/sanctuary This is based on ramda but also covers fantasy-land types.
  • Marcel Lamothe
    Marcel Lamothe over 8 years
    Would it be fair to say ES6 has the capability for lazy evaluation with the introduction of Yield? developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • Scott Sauyet
    Scott Sauyet over 8 years
    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.
  • arcseldon
    arcseldon about 8 years
    @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
    arcseldon about 8 years
    Point taken if comparing to Haskell's language level laziness.
  • Wojciech Bednarski
    Wojciech Bednarski over 5 years
    @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.
  • Scott Sauyet
    Scott Sauyet over 5 years
    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 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.
  • przemo_li
    przemo_li over 3 years
    @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.