Nick Ribal
Front-end veteran, consultant and freelancer, who's armed with a decade of experience solving challenges on the Web platform for large Internet companies.
The problem with OO languages is they’ve got all this implicit environment that they carry around with them.
You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.
function logger(name, message){
console.info(name + ' says:', message)
}
var namedLogger = R.curry(logger)
var monsterLogger = namedLogger('Cookie monster')
monsterLogger('OM NOM NOM')
monsterLogger('ALL YOUR COOKIES ARE BELONG TO US!')
// >> 'Cookie monster says: OM NOM NOM'
// >> 'Cookie monster says: ALL YOUR COOKIES ARE
// >> BELONG TO US!'
var billLogger = namedLogger('Bill'),
billMessages = ['Bill can curry', 'Be like Bill']
R.forEach(billLogger, billMessages) // side effect
// >> 'Bill says: Bill can curry'
// >> 'Bill says: Be like Bill'
function converter(toUnit, factor, offset, input){
offset = offset || 0
return (offset+input)*factor).toFixed(2) + toUnit
}
var convert = R.curry(converter)
var milesToKm = convert('km', 1.609, null),
poundsToKg = convert('kg', 0.454, null),
farenheitToCelsius = convert('°C', 0.5556, -32)
milesToKm(10)
// >> "16.09km"
poundsToKg(2.5)
// >> "1.14kg"
farenheitToCelsius(98)
// >> "36.67°C"
var DEFAULTS = {
filename: 'webpack-[name].js',
path: '//static.90min.com/assets/',
}
// DEFAULTS are shared and overridable
var CONFS_BY_ENV = {
dev: { path: '//dev.90min.com:8080/assets' },
qa: { filename: 'WEBPACK-[name].QA.js' },
prod: {}, // matches DEFAULTS
}
// auto-currying: R.merge(DEFAULTS) returns a fn,
// which expects a second argument: a specific env
// conf object to override DEFAULTS with. It then
// returns a new object, leaving originals intact.
var CONFS = R.map(R.merge(DEFAULTS), CONFS_BY_ENV)
{
dev: {
filename: 'webpack-[name].js',
path: '//dev.90min.com:8080/assets',
},
qa: {
filename: 'WEBPACK-[name].QA.js',
path: '//static.90min.com/assets/',
},
prod: {
filename: 'webpack-[name].js',
path: '//static.90min.com/assets/',
},
}
var listOfWords = ['foo', 'bar', 'baz'],
first = list => list[0]
first(listOfWords)
// >> 'foo'
var reverse = list => list.reverse()
reverse(listOfWords)
// >> ['baz', 'bar', 'foo']
var last = R.compose(first, reverse)
last(listOfWords)
// >> 'baz'
var lastLeftToRight = R.pipe(reverse, first)
lastLeftToRight(listOfWords)
// >> 'baz'
var first = list => list[0],
reverse = list => list.reverse(),
last = R.compose(first, reverse)
var words = 'foo bar baz'
last(words)
// >> Uncaught TypeError: list.reverse is not
// >> a function
// Because 'list' in reverse() is a String,
// which doesn't have a 'reverse' method on
// it's prototype.
var first = list => list[0],
reverse = list => list.reverse(),
last = R.compose(first, reverse)
var listOfWords = ['foo', 'bar', 'baz']
function lastArg(){ return last(arguments) }
lastArg.apply(null, listOfWords)
// >> Uncaught TypeError: list.reverse is not
// >> a function
// Because 'list' in reverse() is an Arguments
// object, which is an 'Array like' object. It
// lacks Array.prototype's methods (it has
// numeric keys, length and a few other
// properties).
var first = list => R.head(list),
reverse = list => R.reverse(list),
last = R.compose(first, reverse)
function lastArg(){ return last(arguments) }
var listOfWords = ['foo', 'bar', 'baz']
lastArg.apply(null, listOfWords)
// >> 'baz'
var words = R.join(listOfWords, ' ') // 'foo bar baz'
last(words)
// >> 'z'
// Oops, while it works, it's not quite what we meant...
var isntSpace = char => char !== ' ' // <== predicate
// this is the essence of functional composition!
var lastWord = R.pipe(R.takeLastWhile(isntSpace), R.join(''))
lastWord(words)
// >> 'baz'
var responseSample = {
result: "SUCCESS",
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: "Have fun", created: "9/22/2013"},
{ id: 107,
complete: true, priority: "high",
dueDate: "2013-11-22", username: "Mike",
title: "Fix the bug", created: "9/22/2013"},
// , ...
]
}
getIncompleteTaskSummaries("Scott").then(log);
// Will log:
[
{
id: 110,
title: "Rename everything",
dueDate: "2013-11-15",
priority: "medium"
},
{
id: 104,
title: "Do something",
dueDate: "2013-11-29",
priority: "high"
}
]
function getIncompleteTaskSummaries(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.pipeP(
fetchData(),
R.prop('tasks'),
R.filter(R.propEq('username', username)),
R.reject(R.propEq('complete', true))
R.map(
R.pick(
['id', 'dueDate', 'title', 'priority']
)
),
R.sortBy(R.get('dueDate')),
)
getIncompleteTaskSummaries("Scott").then(log)
By Nick Ribal
Learn how easy it is to write functional programming in JS using Ramda.js. Master simple, yet practical FP concepts and apply to your daily work to produce more robust and DRY code. Let’s put the ‘fun’ back in function!
Front-end veteran, consultant and freelancer, who's armed with a decade of experience solving challenges on the Web platform for large Internet companies.