One way to think about it is to think about types versus functions.
Ramda offers a large collection of utility functions. They operate on arrays, on objects, on functions, on strings, on numbers, etc. But they also operate on user-defined types. So in your example, R.map
operates on anything which matches the Functor specification. If the implementation of Either
you use matches that specification, then Ramda's map
will operate on it.
But Ramda does not supply types. It works with the built-in types such as Object, Array, Function, etc. But -- arguably outside Lens
-- it does not supply any types of its own. Libraries such as Folktale provide large collections of types, such as Maybe
, Result
, Validation
, Task
and Future
; more dedicated ones such as Fluture provide powerful versions of one specific type (Future
). All of these types implement the Functor specification. A very incomplete list of such implementations is supplied by FantasyLand.
These two notions, functions on an abstract type and collections of types are complementary. A Ramda function which works on any functor will work on whatever version of Either you use (so long as it matches the specification.) More on this relationship is in this StackOverflow Q+A.
The question compared these two snippets:
Right('boo')
.map(x => x + '!')
.map(x => x.toUpperCase())
and
R.pipe(
R.map(x => x + '!')
R.map(x => x.toUpperCase())
)(Right('boo'))
But neither is how I would think of the problem from a Ramda perspective. Ramda is all about functions. It supplies functions and expects you to use them to build more sophisticated functions, and then to use those to build even higher level functions.
If we wrote this function:
const bigBang = pipe(
map (x => x + '!'),
map (x => x .toUpperCase ())
)
or perhaps this version
const bigBang = pipe (
map (concat (__, '!')),
map (toUpper)
)
Then this function is now available to use on many types. For example:
bigBang (['boo', 'scared', 'you']) //=> ['BOO!', 'SCARED!', 'YOU!']
bigBang ({a: 'boo', b: 'ya'}) //=> {a: 'BOO!', b: 'YA!}
bigBang ((s) => s .repeat (2)) ('boo') //=> 'BOOBOO!'
bigBang (Right ('boo')) //=> Right ('BOO!')
bigBang (Left ('oops')) //=> Left ('oops')
bigBang (Just ('boo')) //=> Just ('BOO!')
bigBang (Nothing()) //=> Nothing ()
bigBang (Future ('boo')) //=> Future ('BOO!')
The first three -- Array, Object, and Function implementations -- are supplied by Ramda. But the others still work since Ramda interoperates with the FantasyLand Functor specification. And it will work if you supply your own map
method on your type (or even better a fantasy-land/map
method.)
So no, you don't need Ramda to work with Monads or with other abstract types. You can work directly with their implementation. But Ramda offers some nice ways to interoperate with them in a generic manner.
.map()
method. Ramda'sR.map()
works with anything that has a.map()
method. This means it works with any Functor. Very very simply the same sort of thing can be implemented asmap = fn = x => x.map(fn)
- you can run this against a plain array, orEither
. Because both conform to the Functor specifications. – Douglasdouglashome