KEMBAR78
Ramda, a functional JavaScript library | PDF
Globalcode – Open4education
Ramda
Derek Stavis
Software Engineer
Globalcode – Open4education
Who?
Derek Stavis
github.com/derekstavis
Software Engineer
Ruby, JavaScript, Python, C
Node, React & Webpack Advocate
Globalcode – Open4education
Have you been using functional
helper libraries?
Globalcode – Open4education
Underscore?
🤔
Globalcode – Open4education
Lodash?
🤔
Globalcode – Open4education
Really?
🙄
Globalcode – Open4education
Those are really good libraries
Globalcode – Open4education
Weren't designed with a functional
mindset from day 0
Globalcode – Open4education
Ramda
A functional programming library
http://ramdajs.com
Globalcode – Open4education
Meet Ramda
Emphasizes a purer functional style
Immutability and side-effect free functions
Ramda functions are automatically curried
Argument ordering convenient to currying
Keep rightmost the data to be operated
Globalcode – Open4education
Small, single-responsibility and
reusable functions
Globalcode – Open4education
Ramda shines on currying
Globalcode – Open4education
Ramda shines on currying
var names = [
{ name: 'Pablo' },
{ name: 'Escobar' },
{ name: 'Gaviria' }
]
var getName = R.prop('name')
var pickNames = R.map(getName, names)
pickNames()
=> [ 'Pablo', 'Escobar', 'Gaviria' ]
Globalcode – Open4education
Ramda shines on currying
var names = [
{ name: 'Pablo' },
{ name: 'Escobar' },
{ name: 'Gaviria' }
]
var getName = R.prop('name')
var pickNames = R.map(getName)
pickNames(names)
=> [ 'Pablo', 'Escobar', 'Gaviria' ]
Globalcode – Open4education
Currying?
😨
Globalcode – Open4education
Currying was named after
Haskell Curry
One of the first to investigate such technique
Globalcode – Open4education
Turn a function that expects
multiple arguments into one that,
when supplied fewer arguments,
returns a new function that awaits
the remaining ones
Globalcode – Open4education
Ramda is about currying
const cookPasta = R.curry((water, heat, pasta) => { ... })
// Have everything needed
cookPasta (water, heat, pasta)
// Forgot to buy pasta
cookPasta (water, heat)(pasta)
// No gas and no pasta
cookPasta (water)(heat, pasta)
// Had to go twice to supermarket
cookPasta (water)(heat)(pasta)
Globalcode – Open4education
This is truly powerful
Globalcode – Open4education
Ramda is all curried
😎
Globalcode – Open4education
Point-free programming
🖖
Globalcode – Open4education
Ramda is all curried
R.add(2, 3) //=> 5
R.add(7)(10) //=> 17
R.contains(2, [1, 2, 3]) //=> true
R.contains(1)([1, 2, 3]) //=> true
R.replace(/foo/g, 'bar', 'foo foo') //=> 'bar bar'
R.replace(/foo/g, 'bar')('foo foo') //=> 'bar bar'
R.replace(/foo/g)('bar')('foo foo') //=> 'bar bar'
Globalcode – Open4education
Apply the parameters left-to-right
when you have them
👉
Globalcode – Open4education
But what if you'd like to apply the
leftmost or intermediate arguments
before the rightmost?
Globalcode – Open4education
Leave argument placeholders
✍
Globalcode – Open4education
Functional placeholder
R.minus(R.__, 5)(10) //=> 5
R.divide(R.__, 5)(10) //=> 17
R.contains(R.__, [1, 2, 3])(2) //=> true
R.contains(R.__, [1, 2, 3])(1) //=> true
R.contains(R.__, R.__)(1)([1, 2, 3]) //=> true
Globalcode – Open4education
Or do partial application
Globalcode – Open4education
Ramda does partial application
both left-to-right and right-to-left
Globalcode – Open4education
In fact, there's no currying as in
theoretical math, Ramda currying
is, in reality, partial application
Globalcode – Open4education
Partial application
const greet = (salutation, title, name) =>
`${salutation}, ${title} ${name}!`
const greetMrCrowley = R.partialRight(greet, ['Mr.', 'Crowley'])
greetMrCrowley('Hello') //=> 'Hello, Mr. Crowley!'
const greetMr = R.partial(greet, ['Hello', 'Mr.'])
const greetMs = R.partial(greet, ['Hello', 'Ms.'])
greetMr('Mendel') // => 'Hello, Mr. Mendel'
greetMs('Curie') // => 'Hello, Ms. Curie'
Globalcode – Open4education
Ramda also makes available some
cool and very useful functions
Globalcode – Open4education
Avoiding you to rewrite the same
stuff that you always have to
Globalcode – Open4education
Ramda functions are divided into
various categories
Globalcode – Open4education
Ramda functions categories
Function
Math
List
Logic
Object
Relation
Globalcode – Open4education
Function functions
// Always return the same thing, ignoring arguments
const name = R.always('Derek')
name('Willian') //=> Derek
// Functional if then else, a.k.a. Maybe Monad
const findName = R.ifElse(
R.has('name'),
R.prop('name'),
R.always('No name')
)
findName({ title: 'Mr.', name: 'Derek' }) //=> Derek
findName({ title: 'Mr.' }) //=> No name
// Pipe functions left-to-right. First function can have any arity
// Others must have arity of exactly one
const calculate = R.pipe(Math.pow, R.negate, R.inc);
calculate(3, 2); // -(3^2) + 1
Globalcode – Open4education
Math functions
// Addition
R.add(1, 2) //=> 3
// Subtraction
R.subtract(2, 1) //=> 1
// Sum all elements
R.sum([1, 1, 1]) //=> 3
// Increment
R.inc(41) //=> 42
// Decrement
R.dec(43) //=> 42
// Calculate the mean
R.mean([2, 7, 9]) //=> 6
Globalcode – Open4education
List functions
// Check if all elements match a predicate
R.all(R.gt(4), [1, 2, 3]) //=> true
// Check if a number is inside a list
R.contains(3, [1, 2, 3])
// Drop elements from start
R.drop(1, ['foo', 'bar', 'baz']) //=> ['bar', 'baz']
// Find elements using a predicate
const list = [{a: 1}, {a: 2}, {a: 3}]
R.find(R.propEq('a', 2))(list) //=> {a: 2}
R.find(R.propEq('a', 4))(list) //=> undefined
// Flatten a list of list into a single list
const list = [1, 2, [3, 4], 5, [6, [7, 8, [9, [10, 11], 12]]]]
R.flatten(list) //=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
Globalcode – Open4education
Logic functions
// Combine predicates into a single function
var gt10 = x => x > 10
var even = x => x % 2 === 0
var gt10even = R.both(gt10, even)
gt10even(100) //=> true
gt10even(101) //=> false
// Functional switch case (aka. pattern matching?)
var whatHappensAtTemperature = R.cond([
[R.equals(0), R.always('water freezes at 0°C')],
[R.equals(100), R.always('water boils at 100°C')],
[R.T, temp => 'nothing special happens at ' + temp + '°C']
])
whatHappensAtTemperature(0) //=> 'water freezes at 0°C'
whatHappensAtTemperature(50) //=> 'nothing special happens at 50°C'
whatHappensAtTemperature(100) //=> 'water boils at 100°C'
Globalcode – Open4education
Object functions
// Add a key and value to an already existing object
R.assoc('c', 3, {a: 1, b: 2}) //=> {a: 1, b: 2, c: 3}
// Remove a key from an already existing object
R.dissoc('b', {a: 1, b: 2, c: 3}) //=> {a: 1, c: 3}
// Do the same with a nested path
R.assocPath(['a', 'b', 'c'], 42, {a: {b: {c: 0}}})
//=> {a: {b: {c: 42}}}
// Check if an object contains a key
var hasName = R.has('name')
hasName({name: 'alice'}) //=> true
hasName({name: 'bob'}) //=> true
hasName({}) //=> false
// Pick some properties from objects
var abby = {name: 'Abby', age: 7, hair: 'blond', grade: 2}
var fred = {name: 'Fred', age: 12, hair: 'brown', grade: 7}
R.project(['name', 'grade'], [abby, fred])
//=> [{name: 'Abby', grade: 2}, {name: 'Fred', grade: 7}]
Globalcode – Open4education
Relation functions
// Lots of comparison functions
// Kinda tricky, but also kinda natural
R.gt(2, 1) // 2 > 1
R.gt(2, 2) // 2 > 2
R.gte(2, 2) // 2 >= 2
R.gte(3, 2) // 3 >= 2
R.lt(2, 1) // 2 < 1
R.lt(2, 2) // 2 < 2
R.lte(2, 2) // 2 <= 2
R.lte(3, 2) // 3 <= 2
// Restrict a number to a range
R.clamp(1, 10, -1) // => 1
R.clamp(1, 10, 11) // => 10
// Intersect two lists
R.intersection([1,2,3,4], [7,6,4,3]) //=> [4, 3]
Globalcode – Open4education
Ramda works well with Promise
Globalcode – Open4education
Ramda and Promises
app.get('/v1/plants/:id', auth.active, (req, res) => {
db.plants.find({ id: req.params.id })
.then(R.head)
.then(R.bind(res.send, res))
})
Globalcode – Open4education
Ramda and Promises
function timesValues (number) {
return spot(number)
.then(spot =>
db.collection('zone_type_values')
.findOne({ id: spot.zone_type_value_id })
.then(R.prop('time_values'))
.then(R.assoc('timesValues', R.__, spot))
.then(R.pickAll(['id', 'name', 'timesValues']))
)
}
Globalcode – Open4education
Thanks for watching
Questions?
github.com/derekstavis
twitter.com/derekstavis
facebook.com/derekstavis

Ramda, a functional JavaScript library

  • 1.
  • 2.
    Globalcode – Open4education Who? DerekStavis github.com/derekstavis Software Engineer Ruby, JavaScript, Python, C Node, React & Webpack Advocate
  • 3.
    Globalcode – Open4education Haveyou been using functional helper libraries?
  • 4.
  • 5.
  • 6.
  • 7.
    Globalcode – Open4education Thoseare really good libraries
  • 8.
    Globalcode – Open4education Weren'tdesigned with a functional mindset from day 0
  • 9.
    Globalcode – Open4education Ramda Afunctional programming library http://ramdajs.com
  • 10.
    Globalcode – Open4education MeetRamda Emphasizes a purer functional style Immutability and side-effect free functions Ramda functions are automatically curried Argument ordering convenient to currying Keep rightmost the data to be operated
  • 11.
    Globalcode – Open4education Small,single-responsibility and reusable functions
  • 12.
  • 13.
    Globalcode – Open4education Ramdashines on currying var names = [ { name: 'Pablo' }, { name: 'Escobar' }, { name: 'Gaviria' } ] var getName = R.prop('name') var pickNames = R.map(getName, names) pickNames() => [ 'Pablo', 'Escobar', 'Gaviria' ]
  • 14.
    Globalcode – Open4education Ramdashines on currying var names = [ { name: 'Pablo' }, { name: 'Escobar' }, { name: 'Gaviria' } ] var getName = R.prop('name') var pickNames = R.map(getName) pickNames(names) => [ 'Pablo', 'Escobar', 'Gaviria' ]
  • 15.
  • 16.
    Globalcode – Open4education Curryingwas named after Haskell Curry One of the first to investigate such technique
  • 17.
    Globalcode – Open4education Turna function that expects multiple arguments into one that, when supplied fewer arguments, returns a new function that awaits the remaining ones
  • 18.
    Globalcode – Open4education Ramdais about currying const cookPasta = R.curry((water, heat, pasta) => { ... }) // Have everything needed cookPasta (water, heat, pasta) // Forgot to buy pasta cookPasta (water, heat)(pasta) // No gas and no pasta cookPasta (water)(heat, pasta) // Had to go twice to supermarket cookPasta (water)(heat)(pasta)
  • 19.
  • 20.
  • 21.
  • 22.
    Globalcode – Open4education Ramdais all curried R.add(2, 3) //=> 5 R.add(7)(10) //=> 17 R.contains(2, [1, 2, 3]) //=> true R.contains(1)([1, 2, 3]) //=> true R.replace(/foo/g, 'bar', 'foo foo') //=> 'bar bar' R.replace(/foo/g, 'bar')('foo foo') //=> 'bar bar' R.replace(/foo/g)('bar')('foo foo') //=> 'bar bar'
  • 23.
    Globalcode – Open4education Applythe parameters left-to-right when you have them 👉
  • 24.
    Globalcode – Open4education Butwhat if you'd like to apply the leftmost or intermediate arguments before the rightmost?
  • 25.
    Globalcode – Open4education Leaveargument placeholders ✍
  • 26.
    Globalcode – Open4education Functionalplaceholder R.minus(R.__, 5)(10) //=> 5 R.divide(R.__, 5)(10) //=> 17 R.contains(R.__, [1, 2, 3])(2) //=> true R.contains(R.__, [1, 2, 3])(1) //=> true R.contains(R.__, R.__)(1)([1, 2, 3]) //=> true
  • 27.
    Globalcode – Open4education Ordo partial application
  • 28.
    Globalcode – Open4education Ramdadoes partial application both left-to-right and right-to-left
  • 29.
    Globalcode – Open4education Infact, there's no currying as in theoretical math, Ramda currying is, in reality, partial application
  • 30.
    Globalcode – Open4education Partialapplication const greet = (salutation, title, name) => `${salutation}, ${title} ${name}!` const greetMrCrowley = R.partialRight(greet, ['Mr.', 'Crowley']) greetMrCrowley('Hello') //=> 'Hello, Mr. Crowley!' const greetMr = R.partial(greet, ['Hello', 'Mr.']) const greetMs = R.partial(greet, ['Hello', 'Ms.']) greetMr('Mendel') // => 'Hello, Mr. Mendel' greetMs('Curie') // => 'Hello, Ms. Curie'
  • 31.
    Globalcode – Open4education Ramdaalso makes available some cool and very useful functions
  • 32.
    Globalcode – Open4education Avoidingyou to rewrite the same stuff that you always have to
  • 33.
    Globalcode – Open4education Ramdafunctions are divided into various categories
  • 34.
    Globalcode – Open4education Ramdafunctions categories Function Math List Logic Object Relation
  • 35.
    Globalcode – Open4education Functionfunctions // Always return the same thing, ignoring arguments const name = R.always('Derek') name('Willian') //=> Derek // Functional if then else, a.k.a. Maybe Monad const findName = R.ifElse( R.has('name'), R.prop('name'), R.always('No name') ) findName({ title: 'Mr.', name: 'Derek' }) //=> Derek findName({ title: 'Mr.' }) //=> No name // Pipe functions left-to-right. First function can have any arity // Others must have arity of exactly one const calculate = R.pipe(Math.pow, R.negate, R.inc); calculate(3, 2); // -(3^2) + 1
  • 36.
    Globalcode – Open4education Mathfunctions // Addition R.add(1, 2) //=> 3 // Subtraction R.subtract(2, 1) //=> 1 // Sum all elements R.sum([1, 1, 1]) //=> 3 // Increment R.inc(41) //=> 42 // Decrement R.dec(43) //=> 42 // Calculate the mean R.mean([2, 7, 9]) //=> 6
  • 37.
    Globalcode – Open4education Listfunctions // Check if all elements match a predicate R.all(R.gt(4), [1, 2, 3]) //=> true // Check if a number is inside a list R.contains(3, [1, 2, 3]) // Drop elements from start R.drop(1, ['foo', 'bar', 'baz']) //=> ['bar', 'baz'] // Find elements using a predicate const list = [{a: 1}, {a: 2}, {a: 3}] R.find(R.propEq('a', 2))(list) //=> {a: 2} R.find(R.propEq('a', 4))(list) //=> undefined // Flatten a list of list into a single list const list = [1, 2, [3, 4], 5, [6, [7, 8, [9, [10, 11], 12]]]] R.flatten(list) //=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
  • 38.
    Globalcode – Open4education Logicfunctions // Combine predicates into a single function var gt10 = x => x > 10 var even = x => x % 2 === 0 var gt10even = R.both(gt10, even) gt10even(100) //=> true gt10even(101) //=> false // Functional switch case (aka. pattern matching?) var whatHappensAtTemperature = R.cond([ [R.equals(0), R.always('water freezes at 0°C')], [R.equals(100), R.always('water boils at 100°C')], [R.T, temp => 'nothing special happens at ' + temp + '°C'] ]) whatHappensAtTemperature(0) //=> 'water freezes at 0°C' whatHappensAtTemperature(50) //=> 'nothing special happens at 50°C' whatHappensAtTemperature(100) //=> 'water boils at 100°C'
  • 39.
    Globalcode – Open4education Objectfunctions // Add a key and value to an already existing object R.assoc('c', 3, {a: 1, b: 2}) //=> {a: 1, b: 2, c: 3} // Remove a key from an already existing object R.dissoc('b', {a: 1, b: 2, c: 3}) //=> {a: 1, c: 3} // Do the same with a nested path R.assocPath(['a', 'b', 'c'], 42, {a: {b: {c: 0}}}) //=> {a: {b: {c: 42}}} // Check if an object contains a key var hasName = R.has('name') hasName({name: 'alice'}) //=> true hasName({name: 'bob'}) //=> true hasName({}) //=> false // Pick some properties from objects var abby = {name: 'Abby', age: 7, hair: 'blond', grade: 2} var fred = {name: 'Fred', age: 12, hair: 'brown', grade: 7} R.project(['name', 'grade'], [abby, fred]) //=> [{name: 'Abby', grade: 2}, {name: 'Fred', grade: 7}]
  • 40.
    Globalcode – Open4education Relationfunctions // Lots of comparison functions // Kinda tricky, but also kinda natural R.gt(2, 1) // 2 > 1 R.gt(2, 2) // 2 > 2 R.gte(2, 2) // 2 >= 2 R.gte(3, 2) // 3 >= 2 R.lt(2, 1) // 2 < 1 R.lt(2, 2) // 2 < 2 R.lte(2, 2) // 2 <= 2 R.lte(3, 2) // 3 <= 2 // Restrict a number to a range R.clamp(1, 10, -1) // => 1 R.clamp(1, 10, 11) // => 10 // Intersect two lists R.intersection([1,2,3,4], [7,6,4,3]) //=> [4, 3]
  • 41.
    Globalcode – Open4education Ramdaworks well with Promise
  • 42.
    Globalcode – Open4education Ramdaand Promises app.get('/v1/plants/:id', auth.active, (req, res) => { db.plants.find({ id: req.params.id }) .then(R.head) .then(R.bind(res.send, res)) })
  • 43.
    Globalcode – Open4education Ramdaand Promises function timesValues (number) { return spot(number) .then(spot => db.collection('zone_type_values') .findOne({ id: spot.zone_type_value_id }) .then(R.prop('time_values')) .then(R.assoc('timesValues', R.__, spot)) .then(R.pickAll(['id', 'name', 'timesValues'])) ) }
  • 44.
    Globalcode – Open4education Thanksfor watching Questions? github.com/derekstavis twitter.com/derekstavis facebook.com/derekstavis