by Jason Lengstorf
@jlengstorf | jason@lengstorf.com
Slides: git.io/vyzvq
(s → a) → ((a, s) → s) → Lens s a
Lens s a = Functor f => (a → f a) → s → f s
(If you do understand this, now would be a good time to fake an urgent phone call.)
var xLens = R.lens(R.prop('x'), R.assoc('x'));
R.view(xLens, {x: 1, y: 2}); //=> 1
R.set(xLens, 4, {x: 1, y: 2}); //=> {x: 4, y: 2}
R.over(xLens, R.negate, {x: 1, y: 2}); //=> {x: -1, y: 2}
const people = [
{
name: 'Marisa',
spirit_animal: 'koala',
beverages: [
'tea',
'vodka', // <-- WTF is this?!
],
},
{
name: 'Jason',
spirit_animal: 'bear',
beverages: [
'coffee',
'whiskey',
],
},
];
First: a quick bit of
jargon vocabulary.
Code that explicitly describes how to do something.
"I'd like the pie heated and I don't want the ice cream on top, I want
it on the side, and I'd like strawberry instead of vanilla if you have
it. If not, then no ice cream, just whipped cream — but only if it's
real; if it's out of the can then nothing."
Code that describes what the result should be.
"Give me all the bacon and eggs you have."
Let’s look at some code to fix that beverage list.
let fixed = [];
for (let person of people) {
if (person.beverages) {
for (let beverage in person.beverages) {
if (person.beverages[beverage] === 'vodka') {
person.beverages[beverage] = 'whiskey (FTFY)';
}
}
}
fixed.push(person);
}
Try this live: https://goo.gl/aobHwP
const fixBeverage = (str) => (
str.replace('vodka', 'whiskey (FTFY)')
);
const helpIfConfused = (person) => ({
...person,
beverages: person.beverages.map(fixBeverage)
});
const fixed = people.map(helpIfConfused);
Try this live: https://goo.gl/HO7tps
[
{
"name": "Marisa",
"spirit_animal": "koala",
"beverages": [
"tea",
"whiskey (FTFY)"
]
},
{
"name": "Jason",
"spirit_animal": "bear",
"beverages": [
"coffee",
"whiskey"
]
}
]
Array.prototype.map()
Applies a function to each element of an array.
const double = num => num * 2;
const numbers = [1, 2, 3];
const nextNumbers = [];
for (let x in numbers) {
nextNumbers[x] = double(numbers[x]);
}
Try this live: https://goo.gl/30SfNQ
const double = num => num * 2;
const numbers = [1, 2, 3];
const nextNumbers = numbers.map(double);
Try this live: https://goo.gl/7mYn2x
Array.prototype.filter()
Array.prototype.sort()
Array.prototype.every()
let myFavoriteThing = 'whiskey';
function describeMyFavoriteThing() {
return `I prefer to drink quality ${myFavoriteThing}.`;
}
Try this live: https://goo.gl/IQeD45
function clarifyFavoriteThing() {
myFavoriteThing = 'aged ' + myFavoriteThing;
}
Try this live: https://goo.gl/FEUtUZ
function makeFamilyFriendly() {
myFavoriteThing = 'scented bubble bath';
}
Try this live: https://goo.gl/zgRpEm
let myFavoriteThing = 'whiskey';
clarifyFavoriteThing();
describeMyFavoriteThing();
//=>"I prefer to drink quality aged whiskey." (yay!)
// ...probably a bunch of additional code...
makeFamilyFriendly();
// ...probably more additional code...
describeMyFavoriteThing();
//=>"I prefer to drink quality scented bubble bath." (!)
Try this live: https://goo.gl/xFPtJF
function describeMyFavoriteThing(beverage) {
return `I prefer to drink quality ${beverage}.`;
}
function clarifyFavoriteThing(favoriteThing) {
return `aged ${favoriteThing}`;
}
function makeFamilyFriendly() {
return 'scented bubble bath';
}
Try this live: https://goo.gl/qDtJeu
const myFavoriteThing = 'whiskey';
const clarified = clarifyFavoriteThing(myFavoriteThing);
const newFavorite = makeFamilyFriendly();
describeMyFavoriteThing(myFavoriteThing);
//=>"I prefer to drink quality whiskey."
describeMyFavoriteThing(clarified);
//=>"I prefer to drink quality aged whiskey."
describeMyFavoriteThing(newFavorite);
//=>"I prefer to drink quality scented bubble bath."
Try this live: https://goo.gl/qDtJeu
// start debugging here: ↓ ↓ ↓ ↓ ↓
describeMyFavoriteThing(newFavorite);
Try this live: https://goo.gl/qDtJeu
This means the function call can always be replaced with its return value without breaking the program.
const chz = `I love ${clarifyFavoriteThing('cheddar')}`;
...is the same as...
const chz = 'I love aged cheddar.';
expect(clarifyFavoriteThing('cheddar'))
.toEqual('aged cheddar');
function describeMyFavoriteThing(beverage) {
return `I prefer to drink quality ${beverage}.`;
}
function clarifyFavoriteThing(favoriteThing) {
return `aged ${favoriteThing}`;
}
const showClarifiedFavorite = R.compose(
describeMyFavoriteThing,
clarifyFavoriteThing
);
const result = showClarifiedFavorite('whiskey');
Try this live:https://goo.gl/wQFhZK
describeMyFavoriteThing(clarifyFavoriteThing('whiskey'));
It just means we don't need the data up front.
const albums = [
{
name: 'Middle Cyclone',
artist: 'Neko Case',
genre: 'indie',
},
{
name: 'Highly Refined Pirates',
artist: 'Minus The Bear',
genre: 'rock',
},
{
name: 'Rabbit Fur Coat',
artist: 'Jenny Lewis',
genre: 'indie',
},
{
name: 'Black on Both Sides',
artist: 'Mos Def',
genre: 'hip-hop',
},
];
function getOnlyIndie(albums) {
let filtered = [];
for (let album of albums) {
if (album.genre === 'indie') {
filtered.push(album);
}
}
filtered.sort((album1, album2) => {
if (album1.artist === album2.artist) return 0;
return album1.artist > album2.artist ? 1 : -1;
});
return filtered;
}
Try this live: https://goo.gl/lPK1CD
const byArtistAsc = (album1, album2) => {
if (album1.artist === album2.artist) {
return 0;
}
return album1.artist > album2.artist ? 1 : -1;
};
const getOnlyIndie = album => album.genre === 'indie';
albums.filter(getOnlyIndie).sort(byArtistAsc);
Try this live: https://goo.gl/HXscSK
We can do even better.
const filterByGenre = R.curry((genre, album) => {
return album.genre === genre;
});
// Create similar functions without duplicate code
const onlyHipHop = filterByGenre('hip-hop');
const onlyIndie = filterByGenre('indie');
const onlyRock = filterByGenre('rock');
const hipHop = albums.filter(onlyHipHop);
Try this live: https://goo.gl/qPJhZW
R.curry()
Allows functions to be called in stages.
function addNumbers(num1, num2) {
return num1 + num2;
}
const curriedAdd = R.curry(addNumbers);
const add4 = curriedAdd(4);
add4(2); //=> 6
add4(7); //=> 11
Try this live: https://goo.gl/jVjOW3
...but just these three techniques will save you hours of headaches and make your code better and easier to deal with going forward.
Jason Lengstorf
@jlengstorf | jason@lengstorf.com
Tweet: @jlengstorf #webrebels