Ramda: Practical Functional Javascript
- first class functions
- higher order functions
- purity
- immutability
- composition
- partial application & curry
- recursion & optimization
- Lazy evaluation
Reduce
Map
Filter
Recognize most loops are one of
another _ ???
Ramda is NOT a drop-in replacement for Underscore (or LoDash). It is intended to work with a different style of coding.
Underscore/LoDash
_.map([1, 2, 3], multiply3);
// → [3, 6, 9]
-
Ramda methods are automatically curried.
-
Ramda takes the function(s) first, and the data last.
Ramda
R.map(multiply3, [1, 2, 3]);
// → [3, 6, 9]
R.curry
R.compose
Currying
Currying is the process of turning a function that expects multiple parameters into one that, when supplied fewer parameters, returns a new function that awaits the remaining ones.
var getProperty = R.curry(function(prop, obj) {
return obj[prop];
});
var getProperty = function(prop, obj) {
return obj[prop];
};
var name = getProperty('name', {'name' : 'damillionaire'});
var getName = getProperty('name');
var double = R.multiply(2);
double(13); // → 26
var greater = function(a,b) {
return a > b ? a : b;
};
var max = R.reduce(greater, -Infinity);
max([1,-3483,9,7,2]) // 9
max([-21,-3483,-2,-1]) // -1
R.multiply(2, 13); // → 26
_ vs Ramda
underscore/lodash
var firstTwoLetters = function(words) {
return _.map(words, function(word) {
return _.first(word, 2);
});
}
firstTwoLetters(['andy', 'nero']);
// [‘an’, ‘ne’]
Ramda.js
var firstTwoLetters = R.map(R.first(2));
firstTwoLetters(['andy', 'nero']);
// [‘an’, ‘ne’]
_'s API prevents you from currying, b/c its arguments are backwards
compose
function compose(g, f) {
return function(x) {
return g(f(x));
};
}
var wordCount = R.compose(R.length, R.split(' '));
wordCount("There are seven words in this sentence"); // 7
var wordCount = function(str) {
var words = _.split(' ', str);
return _.length(words);
}
wordCount("There are seven words in this sentence"); // 7
compose
"Cool. But does it do promises?"
R.composeP()
R.pipeP()
var data = {
result: "SUCCESS",
interfaceVersion: "1.0.3",
requested: "10/17/2013 15:31:20",
lastUpdated: "10/16/2013 10:52:39",
tasks: [
{id: 104, complete: false, priority: "high",
dueDate: "2013-11-29", username: "Scott",
title: "Do something", created: "9/22/2013"},
{id: 105, complete: false, priority: "medium",
dueDate: "2013-11-22", username: "Lena",
title: "Do something else", created: "9/22/2013"},
{id: 107, complete: true, priority: "high",
dueDate: "2013-11-22", username: "Mike",
title: "Fix the foo", created: "9/22/2013"},
{id: 108, complete: false, priority: "low",
dueDate: "2013-11-15", username: "Punam",
title: "Adjust the bar", created: "9/25/2013"},
{id: 110, complete: false, priority: "medium",
dueDate: "2013-11-15", username: "Scott",
title: "Rename everything", created: "10/2/2013"},
{id: 112, complete: true, priority: "high",
dueDate: "2013-11-27", username: "Lena",
title: "Alter all quuxes", created: "10/5/2013"}
// , ...
]
};
Suppose we expect to get some data like this:
getIncompleteTaskSummaries("Scott").then(log, log);
// OUTPUT
[
{id: 110, title: "Rename everything",
dueDate: "2013-11-15", priority: "medium"},
{id: 104, title: "Do something",
dueDate: "2013-11-29", priority: "high"}
]
getIncompleteTaskSummaries = function(membername) {
return fetchData()
.then(function(data) {
return data.tasks;
})
.then(function(tasks) {
var results = [];
for (var i = 0, len = tasks.length; i < len; i++) {
if (tasks[i].username == membername) {
results.push(tasks[i]);
}
}
return results;
})
.then(function(tasks) {
var results = [];
for (var i = 0, len = tasks.length; i < len; i++) {
if (!tasks[i].complete) {
results.push(tasks[i]);
}
}
return results;
})
.then(function(tasks) {
var results = [], task;
for (var i = 0, len = tasks.length; i < len; i++) {
task = tasks[i];
results.push({
id: task.id,
dueDate: task.dueDate,
title: task.title,
priority: task.priority
})
}
return results;
})
.then(function(tasks) {
tasks.sort(function(first, second) {
var a = first.dueDate, b = second.dueDate;
return a < b ? -1 : a > b ? 1 : 0;
});
return tasks;
});
};
var getIncompleteTaskSummaries =
R.composeP(R.sortBy(R.get('dueDate')),
R.map(R.pick(['id', 'dueDate', 'title', 'priority'])),
R.reject(R.propEq('complete', true))
R.filter(R.propEq('username', username)),
R.prop('tasks'),
fetchData);
getIncompleteTaskSummaries("Scott").then(log, log)
var sortByDueDate = R.composeP(R.sortBy(R.porp('dueDate')));
var getRequiredFields = R.composeP(R.map(R.pick(['id', 'dueDate', 'title', 'priority'])));
var removeStatusComplete = R.composeP(R.reject(R.propEq('complete', true));
var filterUsername = R.filter(R.propEq('username', username));
var getTasks = R.prop('tasks');
var getIncompleteTaskSummaries =
R.pCompose(sortByDueDate,
getRequiredFields,
removeStatusComplete)
filterUsername,
getTasks,
fetchData);
getIncompleteTaskSummaries("Scott").then(log, log)
function log(x) {
console.log(x);
return x;
};
var getIncompleteTaskSummaries =
R.pCompose(sortByDueDate,
getRequiredFields,
removeStatusComplete)
log,
filterUsername,
getTasks,
log,
fetchData);
getIncompleteTaskSummaries("Scott").then(log, log)
"var vanillia = ['10', '10', '10'];"
"vanilia.map(parseInt)"
[10, NaN, 2]
"_.map(['10','10','10'], parseInt);"
[10, NaN, 2]
"lodash.map(['10','10','10'], parseInt);"
[10, NaN, 2]
"R.map(parseInt, ['10', '10', '10']);"
[10, 10, 10]
-
Encourages lean functions
-
More declarative
-
More testable code
-
Build new functions from other functions
-
Avoid specifying sequence
-
Avoid unnecessary variables and functions
-
Mathematically backed
why CURRY / COMPOSE?
map, reduce, filter
+ Functor's
=
Ramda
jsBin playgrounds
Video's
Related Libraries
Resources
</>
01 function curry(fn) {
02 return function() {
03 if (fn.length > arguments.length) {
04 var slice = Array.prototype.slice;
05 var args = slice.apply(arguments);
06 return function() {
07 return fn.apply(
08 null, args.concat(slice.apply(arguments)));
09 };
10 }
11 return fn.apply(null, arguments);
12 };
13 }
var setResults = setHtml('#results');
var renderVideos = R.map(render);
var getVideos = R.pCompose(R.prop('entry'),
R.prop('feed'),
R.prop('data'));
var queryYoutube = R.pCompose(setResults,
renderVideos,
getVideos,
http.getJSON);
queryYoutube(query).then(log, log);
Ramda.js - curry & compose
By andydrew
Ramda.js - curry & compose
Functional Programming in Javascript using Ramda
- 10,311