These are my crib notes for the talk I'm giving at Hamilton's CoderCamp, June 14, 2017 at Phesant Plucker.
- Ramda is a functional programming framework for JavaScript
- It focuses on small, composable, reusable functions, and encourages a point-free style
- Features:
- Higer-order functions (functions that take another function as an input, OR return a function as the output)
- Function-first, data last, API
- Automatic currying
- Function composition
- Immutability
- All internal code is ES3-compliant (works back to IE6)
- No dependencies! https://github.com/ramda/ramda/blob/master/package.json#L55
- So, let's talk about currying...
- Change to a .map
- Require Ramda
- Use R.multiply(1.10, plans[i].price)
- Show auto-currying (R.multiply(1.10, plans[i].price) => R.multiply(1.10)(plans[i].price))
- Extract multiplyer
- Contrived example; Could do more, but let's jump ahead
-
Filter out only videos w/ views
youtubeVideos = youtubeVideos.filter(video => 'views' in video);
-
Map out the views of the videos
youtubeVideos = youtubeVideos.map(video => video.views);
-
'Combine' these two steps
-
const R = require('ramda')
-
Use R.pipe:
R.pipe(
R.filter(...),
R.map(...)
)(youtubeVideos)
- They accept the function as the first argument, and the thing that you want to applied as the second argument
- Change filter internal to R.has('views')
- Change map internal to R.prop('views')
- Ramda encourages a 'point-free' style; Combine functions so you're dealing with the data less
- To make it point free, and the function only takes one argumetn, we can change it so that:
const getViews = R.pipe...
- Show compose (reverse order)
- Break it up so it's one string of if/else statements
-
NOTE: We want to make things as similar as possible to help with refactoring
- Bring in Ramda
- R.is(ErrorWithStatus, error); R.is(ValidationError, error)
- R.propEq('type', 'PaymentNotIncluded', error)
- But
if error is null || undefined
, R.propEq will fail, so add to the top:
if (!Boolean(error)) {
return 500;
}
R.test(/not found/i, error)
R.always(true, error)
- Now all functions have the same last argument! This is very curry friendly!
- Convert to Cond expression:
R.cond([
[predicateFunction, transformationFunction],
[R.always(true), errorToStatusCodeOrig]
])(error);
- predicateFunction takes a value and returns true or false.
- transformationFunction takes that same value and transforms it in some way
- Goes from top to bottom
- [R.isNil, R.always(500)], [R.is(ErrorWithStatus), R.prop('status')], [R.is(ValidationError), R.always(400)], [R.propEq('type', 'PaymentNotIncluded'), R.always(402)], [R.test(/not found/i), R.always(404)], [R.always(true), R.always(500)]
- Now, get rid of the error from the function:
const errorToStatusCode = R.cond...
- const R = require('ramda')
- Build lenses for artist & name
- Compose the lenses together
- Refactor setArtist: 4a. return R.set(artistNameLens, artistName, album)
- Refactor addMetadata: 5a. return R.over(artistLens, R.merge(metadata), album)
- Refactor getArtist: 6a. return R.view(artistNameLens, album)
- Remove shallowClone
- Let Ramda build the functions! 8a. const setArtist = R.set(artistNameLens); 8b. const getArtist = R.view(artistNameLens);
https://github.com/ramda/ramda/wiki/What-Function-Should-I-Use%3F