Ramdajs

javascript-libraries

https://github.com/ramda/ramda
http://ramdajs.com/docs/
http://buzzdecafe.github.io/code/2014/05/16/introducing-ramda/ - done reading
http://fr.umio.us/why-ramda/ - done reading
http://fr.umio.us/favoring-curry/ - continue reading from 'Booooooring! What Can You Do For ME? '
https://www.sitepoint.com/functional-programming-with-ramda
http://www.youtube.com/watch?v=m3svKOdZijA
http://hughfdjackson.com/javascript/why-curry-helps/
https://rawgit.com/ramda/ramda/master/docs/ramda.html
http://bahmutov.calepin.co/put-callback-first-for-elegance.html
http://en.wikipedia.org/wiki/Take_Five
http://jsperf.com/ramda-auto-curry-cost/9
http://fr.umio.us/the-philosophy-of-ramda/
http://fr.umio.us/ranging-near-and-far/
http://fr.umio.us/iffy-literals/

Why should we use Ramda instead of Underscore?

1. Ramda takes the function first, and the data last. Brian Lonsdorf explains why this parameter ordering is a big deal. In a nutshell, the combination of currying and function-first enables the developer to compose functions with very little code (often in a “point-free” fashion), before finally passing in the data. So instead of this:

// Underscore/Lodash style:
var validUsersNamedBuzz = function(users) {
    return _.filter(users, function(user) { 
        return user.name === 'Buzz' && _.isEmpty(user.errors); 
    });
};

… you can do this:

// Ramda style:
var validUsersNamedBuzz = R.filter(R.where({name: 'Buzz', errors: R.isEmpty}));

I supposed that validUsersNamedBuzz is a function that would takes an array of users, filter this array and return all the users whose name is 'Buzz' and there is not errors (user.errors is empty). In either cases, the validUsersNamedBuzz function takes and array of users, however, in the Ramda example we do not need to use an anonymous inner function, and the condition is specified as object, or something else is going on here.

Ramda code just gives us a function. We still have to call it with the list of tasks in order to get the filtered set. And that's the point. Because we now have a function we can easily combine it with others to operate on whatever sets of data we choose.

Comparing the Underscore/LoDash example with the Ramda example, the Ramda example is much shorter.

2. Ramda methods are automatically curried. While you can curry (or partially apply) functions in Underscore and Lodash, Ramda does it for you. Virtually all methods of 2 or more arguments are curried by default in Ramda. For example:

// `prop` takes two arguments. If I just give it one, I get a function back
var moo = R.prop('moo');
// when I call that function with one argument, I get the result.
var value = moo({moo: 'cow'}); // => 'cow'

This auto-currying makes it easy to compose functions to create new functions. Because the API is function-first, data-last, you can continue composing and composing until you build up the function you need before dropping in the data. (Hugh Jackson published an excellent article describing the advantages of this style.)

// take an object with an `amount` property
// add one to it
// find its remainder when divided by 7
var amtAdd1Mod7 = R.compose(R.moduloBy(7), R.add(1), R.prop('amount'));

// we can use that as is:
amtAdd1Mod7({amount: 17}); // => 4
amtAdd1Mod7({amount: 987}); // => 1
amtAdd1Mod7({amount: 68}); // => 6
// etc. 

// But we can also use our composed function on a list of objects, e.g. to `map`:
var amountObjects = [
    {amount: 903}, {amount: 2875654}, {amount: 6}
]
R.map(amtAdd1Mod7, amountObjects); // => [1, 6, 0]

// of course, `map` is also curried, so you can generate a new function 
// using `amtAdd1Mod7` that will wait for a list of "amountObjects" to 
// get passed in:
var amountsToValue = map(amtAdd1Mod7);
amountsToValue(amountObjects); // => [1, 6, 0]

What is the purpose of R.compose?

var incomplete = R.filter(R.where({complete: false});
// incomplete is now a function that takes an array of TODO tasks

This Ramda code just gives us a function. We'd still have to call it with the list of tasks in order to get the filtered set. And that's the point. Because we now have a function we can easily combine it with others to operate on whatever sets of data we choose. Imagine we had a function groupByUser that grouped the TODO items by user. Then we could simply create a new function:

var activeByUser = R.compose(groupByUser, incomplete);

which selects the incomplete tasks and groups them by user. Or it would, if we ever got around to supplying it with data, because, again, this is simply a function. If we were to write it out by hand, it might look something like this:

// (if created by hand)
var activeByUser = function(tasks) {
    return groupByUser(incomplete(tasks));
};

That we don't have to do it by hand is the point of composition. And composition is one key technique of functional programming. Let's see what happens if we carry it a little further. What if we then need to sort each of these users' TODO lists by due date?

var sortUserTasks = R.compose(R.map(R.sortBy(R.prop("dueDate"))), activeByUser);

The observant reader might have noticed that we could combine all the above. Since our compose function allows more than two parameters, why not do all of this in a single step?

var sortUserTasks = R.compose(
    R.mapObj(R.sortBy(R.prop('dueDate'))),
    groupByUser,
    R.filter(R.where({complete: false})
);

How to use Ramda with Node?

npm install ramda
var R = require('ramda');

How to use Ramda in the browser?

Include it into your page just like you do for any other JavaScript file:

<script src="path/to/yourCopyOf/ramda.js"></script>
<script src="path/to/yourCopyOf/ramda.min.js"></script>
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License