FP in JS-Land
tweeter :: @RobertWPearce
github :: rpearce
email :: me@robertwpearce.com
@RobertWPearce 1
Overview
@RobertWPearce 2
Overview
1.
✅
FP-enablers
@RobertWPearce 2
Overview
1.
✅
FP-enablers
2.
✈
30,000ft view of FP tools
@RobertWPearce 2
Overview
1.
✅
FP-enablers
2.
✈
30,000ft view of FP tools
3.
#
ramda.js
@RobertWPearce 2
Overview
1.
✅
FP-enablers
2.
✈
30,000ft view of FP tools
3.
#
ramda.js
4.
$
crocks.js
@RobertWPearce 2
Overview
1.
✅
FP-enablers
2.
✈
30,000ft view of FP tools
3.
#
ramda.js
4.
$
crocks.js
@RobertWPearce 3
✅
Pass functions as values
const handleClick = e => {
console.log('click event: ', e)
}
document.addEventListener('click', handleClick)
@RobertWPearce 4
✅
Pass functions as values
const handleClick = e => {
console.log('click event: ', e)
}
document.addEventListener('click', handleClick)
@RobertWPearce 4
✅
Pass functions as values
const handleClick = e => {
console.log('click event: ', e)
}
document.addEventListener('click', handleClick)
@RobertWPearce 4
✅
Partial application & currying
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
@RobertWPearce 5
✅
Partial application & currying
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
@RobertWPearce 5
✅
Partial application & currying
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
@RobertWPearce 5
✅
Partial application & currying
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
@RobertWPearce 5
✅
Partial application & currying
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
@RobertWPearce 5
✅
Partial application & currying
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
@RobertWPearce 5
✅
Partial application & currying
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
@RobertWPearce 5
✅
Composition
const add = a => b => b + a
const add2 = add(2)
const sub = a => b => b - a
const sub1 = sub(1)
const mult = a => b => b * a
const mult10 = mult(10)
mult10(sub1(add2(9))) // 100
@RobertWPearce 6
✅
Composition
const add = a => b => b + a
const add2 = add(2)
const sub = a => b => b - a
const sub1 = sub(1)
const mult = a => b => b * a
const mult10 = mult(10)
mult10(sub1(add2(9))) // 100
@RobertWPearce 6
✅
Composition
const add = a => b => b + a
const add2 = add(2)
const sub = a => b => b - a
const sub1 = sub(1)
const mult = a => b => b * a
const mult10 = mult(10)
mult10(sub1(add2(9))) // 100
@RobertWPearce 6
✅
Composition
const add = a => b => b + a
const add2 = add(2)
const sub = a => b => b - a
const sub1 = sub(1)
const mult = a => b => b * a
const mult10 = mult(10)
mult10(sub1(add2(9))) // 100
@RobertWPearce 6
✅
Composition
const add = a => b => b + a
const add2 = add(2)
const sub = a => b => b - a
const sub1 = sub(1)
const mult = a => b => b * a
const mult10 = mult(10)
mult10(sub1(add2(9))) // 100
@RobertWPearce 6
✅
map
const films = [
{ title: 'The Empire Strikes Back', rating: 8.8 },
{ title: 'Pulp Fiction', rating: 8.9 },
{ title: 'The Deer Hunter', rating: 8.2 },
{ title: 'The Lion King', rating: 8.5 }
]
// filmHtml :: Film -> Html
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
films.map(filmHtml)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// "<div>The Deer Hunter, <strong>8.2</strong></div>",
// "<div>The Lion King, <strong>8.5</strong></div>"
// ]
@RobertWPearce 7
✅
map
const films = [
{ title: 'The Empire Strikes Back', rating: 8.8 },
{ title: 'Pulp Fiction', rating: 8.9 },
{ title: 'The Deer Hunter', rating: 8.2 },
{ title: 'The Lion King', rating: 8.5 }
]
// filmHtml :: Film -> Html
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
films.map(filmHtml)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// "<div>The Deer Hunter, <strong>8.2</strong></div>",
// "<div>The Lion King, <strong>8.5</strong></div>"
// ]
@RobertWPearce 7
✅
map
const films = [
{ title: 'The Empire Strikes Back', rating: 8.8 },
{ title: 'Pulp Fiction', rating: 8.9 },
{ title: 'The Deer Hunter', rating: 8.2 },
{ title: 'The Lion King', rating: 8.5 }
]
// filmHtml :: Film -> Html
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
films.map(filmHtml)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// "<div>The Deer Hunter, <strong>8.2</strong></div>",
// "<div>The Lion King, <strong>8.5</strong></div>"
// ]
@RobertWPearce 7
✅
map
const films = [
{ title: 'The Empire Strikes Back', rating: 8.8 },
{ title: 'Pulp Fiction', rating: 8.9 },
{ title: 'The Deer Hunter', rating: 8.2 },
{ title: 'The Lion King', rating: 8.5 }
]
// filmHtml :: Film -> Html
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
films.map(filmHtml)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// "<div>The Deer Hunter, <strong>8.2</strong></div>",
// "<div>The Lion King, <strong>8.5</strong></div>"
// ]
@RobertWPearce 7
✅
map
const films = [
{ title: 'The Empire Strikes Back', rating: 8.8 },
{ title: 'Pulp Fiction', rating: 8.9 },
{ title: 'The Deer Hunter', rating: 8.2 },
{ title: 'The Lion King', rating: 8.5 }
]
// filmHtml :: Film -> Html
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
films.map(filmHtml)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// "<div>The Deer Hunter, <strong>8.2</strong></div>",
// "<div>The Lion King, <strong>8.5</strong></div>"
// ]
@RobertWPearce 7
✅
filter
const films = // ...
// hasHighScore :: Film -> Bool
const hasHighScore = film =>
film.rating >= 8.8
films
.filter(hasHighScore)
.map(filmHtml)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// ]
@RobertWPearce 8
✅
filter
const films = // ...
// hasHighScore :: Film -> Bool
const hasHighScore = film =>
film.rating >= 8.8
films
.filter(hasHighScore)
.map(filmHtml)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// ]
@RobertWPearce 8
✅
filter
const films = // ...
// hasHighScore :: Film -> Bool
const hasHighScore = film =>
film.rating >= 8.8
films
.filter(hasHighScore)
.map(filmHtml)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// ]
@RobertWPearce 8
✅
filter
const films = // ...
// hasHighScore :: Film -> Bool
const hasHighScore = film =>
film.rating >= 8.8
films
.filter(hasHighScore)
.map(filmHtml)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// ]
@RobertWPearce 8
✅
filter
const films = // ...
// hasHighScore :: Film -> Bool
const hasHighScore = film =>
film.rating >= 8.8
films
.filter(hasHighScore)
.map(filmHtml)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// ]
@RobertWPearce 8
✅
reduce (fold)
const films = // ...
// highScoresHtml :: ([Html], Film) -> [Html]
const highScoresHtml = (acc, film) =>
hasHighScore(film)
? acc.concat(filmHtml(film))
: acc
films.reduce(highScoresHtml, [])
@RobertWPearce 9
✅
reduce (fold)
const films = // ...
// highScoresHtml :: ([Html], Film) -> [Html]
const highScoresHtml = (acc, film) =>
hasHighScore(film)
? acc.concat(filmHtml(film))
: acc
films.reduce(highScoresHtml, [])
@RobertWPearce 9
✅
reduce (fold)
const films = // ...
// highScoresHtml :: ([Html], Film) -> [Html]
const highScoresHtml = (acc, film) =>
hasHighScore(film)
? acc.concat(filmHtml(film))
: acc
films.reduce(highScoresHtml, [])
@RobertWPearce 9
Not in vanilla JS
@RobertWPearce 10
Not in vanilla JS
—
❌
pattern matching
@RobertWPearce 10
Not in vanilla JS
—
❌
pattern matching
—
❌
compile-time type checking
@RobertWPearce 10
Not in vanilla JS
—
❌
pattern matching
—
❌
compile-time type checking
—
❌
automatic function currying
@RobertWPearce 10
Not in vanilla JS
—
❌
pattern matching
—
❌
compile-time type checking
—
❌
automatic function currying
—
❌
easy variadic function composition
@RobertWPearce 10
Not in vanilla JS
—
❌
pattern matching
—
❌
compile-time type checking
—
❌
automatic function currying
—
❌
easy variadic function composition
—
❌
algebraic data types
@RobertWPearce 10
Not in vanilla JS
—
❌
pattern matching
—
❌
compile-time type checking
—
❌
automatic function currying
—
❌
easy variadic function composition
—
❌
algebraic data types
—
❌
______ (insert nice FP thing here)
@RobertWPearce 10
Overview
1.
✅
FP-enablers
2.
✈
30,000! view of FP tools
3.
#
ramda.js
4.
$
crocks.js
@RobertWPearce 11
Libraries, tools & education
— Fantasy Land Specification
— Professor Frisby's Mostly Adequate Guide to Functional Programming
— ramda
— lodash/fp
— immutable.js
— folktale
— CycleJS
— RxJS
— flyd
— fp-ts: Functional programming in TypeScript
— crocks
@RobertWPearce 12
Compile-time type-checking
— TypeScript (type-checker & transpiler)
— Flow (type-checker)
@RobertWPearce 13
Tools with courses on egghead.io:
— CycleJS
— RxJS
— immutable.js
— TypeScript
— Flow
— ramda
— crocks
@RobertWPearce 14
Overview
1.
✅
FP-enablers
2.
✈
30,000ft view of FP tools
3.
#
ramda.js
4.
$
crocks.js
@RobertWPearce 15
"Practical, functional JavaScript."
— ramdajs.com
@RobertWPearce • Photo by Kirill Zakharov on Unsplash 16
!
ramda
@RobertWPearce • Photo by George Hiles on Unsplash 17
!
ramda
— easier to create functional pipelines
@RobertWPearce • Photo by George Hiles on Unsplash 17
!
ramda
— easier to create functional pipelines
— immutability
@RobertWPearce • Photo by George Hiles on Unsplash 17
!
ramda
— easier to create functional pipelines
— immutability
— side-effect free code
@RobertWPearce • Photo by George Hiles on Unsplash 17
!
ramda
— easier to create functional pipelines
— immutability
— side-effect free code
— all functions curried
@RobertWPearce • Photo by George Hiles on Unsplash 17
!
ramda
— easier to create functional pipelines
— immutability
— side-effect free code
— all functions curried
— provide data last
@RobertWPearce • Photo by George Hiles on Unsplash 17
refactor with
!
ramda
mult10(sub1(add2(9))) // 100
@RobertWPearce 18
refactor with
!
ramda
// mult10(sub1(add2(9))) // 100
import { compose } from 'ramda'
@RobertWPearce 19
refactor with
!
ramda
// mult10(sub1(add2(9))) // 100
import { compose } from 'ramda'
compose(mult10, sub1, add2)(9) // 100
@RobertWPearce 20
refactor with
!
ramda
// mult10(sub1(add2(9))) // 100
import { compose } from 'ramda'
// compose(mult10, sub1, add2)(9) // 100
const doMaths =
compose(mult10, sub1, add2)
doMaths(9) // 100
@RobertWPearce 21
refactor with
!
ramda #2
films.filter(hasHighScore).map(filmHtml)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// ]
@RobertWPearce 22
refactor with
!
ramda #2
// films.filter(hasHighScore).map(filmHtml)
import { compose, filter, map } from 'ramda'
@RobertWPearce 23
refactor with
!
ramda #2
// films.filter(hasHighScore).map(filmHtml)
import { compose, filter, map } from 'ramda'
compose(map(filmHtml), filter(hasHighScore))
@RobertWPearce 24
refactor with
!
ramda #2
// films.filter(hasHighScore).map(filmHtml)
import { compose, filter, map } from 'ramda'
// output :: [Film] -> [Html]
const output =
compose(map(filmHtml), filter(hasHighScore))
@RobertWPearce 25
refactor with
!
ramda #2
// films.filter(hasHighScore).map(filmHtml)
import { compose, filter, map } from 'ramda'
// output :: [Film] -> [Html]
const output =
compose(map(filmHtml), filter(hasHighScore))
output(films)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// ]
@RobertWPearce 26
refactor with
!
ramda #2
// films.filter(hasHighScore).map(filmHtml)
import { compose, filter, map } from 'ramda'
// output :: [Film] -> [Html]
const output =
compose(map(filmHtml), filter(hasHighScore))
output(films)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// ]
@RobertWPearce 26
refactor with
!
ramda #2
// films.filter(hasHighScore).map(filmHtml)
import { compose, filter, map } from 'ramda'
// filmsToHtml :: [Film] -> [Html]
const filmsToHtml =
map(filmHtml)
// highScores :: [Film] -> [Film]
const highScores =
filter(hasHighScore)
// output :: [Film] -> [Html]
const output =
compose(filmsToHtml, highScores)
output(films)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>",
// ]
@RobertWPearce 27
!
ramda example: goodbye if + else if & switch
// getComponentType :: Props -> <Component />
const getComponentType = props => {
if (props.field) {
return Field
} else if (props.pill) {
return Pill
} else if (props.pillWithLabel) {
return PillWithLabel
} else if (props.withIcon) {
return ValueWithIcon
} else if (props.withLabel) {
return ValueWithLabel
} else {
return Value
}
}
// ...
<MyPolymorphicComponent pillWithLabel {...otherProps} />
// ...
const Component = getComponentType(props)
return <Component {...props} />
@RobertWPearce 28
!
ramda example: goodbye if + else if & switch
// getComponentType :: Props -> <Component />
const getComponentType = props => {
if (props.field) {
return Field
} else if (props.pill) {
return Pill
} else if (props.pillWithLabel) {
return PillWithLabel
} else if (props.withIcon) {
return ValueWithIcon
} else if (props.withLabel) {
return ValueWithLabel
} else {
return Value
}
}
// ...
<MyPolymorphicComponent pillWithLabel {...otherProps} />
// ...
const Component = getComponentType(props)
return <Component {...props} />
@RobertWPearce 28
!
ramda example: goodbye if + else if & switch
// getComponentType :: Props -> <Component />
const getComponentType = props => {
if (props.field) {
return Field
} else if (props.pill) {
return Pill
} else if (props.pillWithLabel) {
return PillWithLabel
} else if (props.withIcon) {
return ValueWithIcon
} else if (props.withLabel) {
return ValueWithLabel
} else {
return Value
}
}
// ...
<MyPolymorphicComponent pillWithLabel {...otherProps} />
// ...
const Component = getComponentType(props)
return <Component {...props} />
@RobertWPearce 28
!
ramda example: goodbye if + else if & switch
// getComponentType :: Props -> <Component />
const getComponentType = props => {
if (props.field) {
return Field
} else if (props.pill) {
return Pill
} else if (props.pillWithLabel) {
return PillWithLabel
} else if (props.withIcon) {
return ValueWithIcon
} else if (props.withLabel) {
return ValueWithLabel
} else {
return Value
}
}
// ...
<MyPolymorphicComponent pillWithLabel {...otherProps} />
// ...
const Component = getComponentType(props)
return <Component {...props} />
@RobertWPearce 28
!
ramda example: goodbye if + else if & switch
const isType =
propEq(__, true)
// getComponentType :: Props -> <Component />
const getComponentType =
cond([
[ isType('field') , always(Field) ],
[ isType('pill') , always(Pill) ],
[ isType('pillWithLabel'), always(PillWithLabel) ],
[ isType('withIcon') , always(ValueWithIcon) ],
[ isType('withLabel') , always(ValueWithLabel) ],
[ T , always(Value) ],
])
// ...
<MyPolymorphicComponent pillWithLabel {...otherProps} />
// ...
const Component = getComponentType(props)
return <Component {...props} />
@RobertWPearce 29
!
ramda example: converting method APIs
document.querySelectorAll('div').length // 76
@RobertWPearce 30
!
ramda example: converting method APIs
import { compose, invoker, length } from 'ramda'
const querySelectorAll =
invoker(1, 'querySelectorAll')
const countDivs =
compose(length, querySelectorAll('div'))
countDivs(document) // 76
@RobertWPearce 31
!
ramda example: converting method APIs
import { compose, invoker, length } from 'ramda'
const querySelectorAll =
invoker(1, 'querySelectorAll')
const countDivs =
compose(length, querySelectorAll('div'))
countDivs(document) // 76
@RobertWPearce 31
!
ramda example: converting method APIs
import { compose, invoker, length } from 'ramda'
const querySelectorAll =
invoker(1, 'querySelectorAll')
const countDivs =
compose(length, querySelectorAll('div'))
countDivs(document) // 76
@RobertWPearce 31
!
ramda example: converting method APIs
import { compose, invoker, length } from 'ramda'
const querySelectorAll =
invoker(1, 'querySelectorAll')
const countDivs =
compose(length, querySelectorAll('div'))
countDivs(document) // 76
@RobertWPearce 31
!
ramda example: converting method APIs
import { compose, invoker, length } from 'ramda'
const querySelectorAll =
invoker(1, 'querySelectorAll')
const countDivs =
compose(length, querySelectorAll('div'))
countDivs(document) // 76
@RobertWPearce 31
!
ramda has heaps of helpers
— ap
— chain
— concat
— compose & composeK (Kleisli) & composeP (Promises) & pipe family
— curry & curryN
— fromPairs & toPairs
— either & both
— over & evolve & lens & set & view
— invoker
— juxt
— map & filter & reduce
— merge & mergeDeepLeft & mergeDeepRight
— omit
— pick
— AND SO MANY MORE
@RobertWPearce 32
Overview
1.
✅
FP-enablers
2.
✈
30,000ft view of FP tools
3.
#
ramda.js
4.
$
crocks.js
@RobertWPearce 33
A collection of well known
Algebraic Data Types for your utter
enjoyment.
— https://evilsoft.github.io/crocks
@RobertWPearce • Photo by Cel Lisboa on Unsplash 34
!
crocks
@RobertWPearce 35
!
crocks
— Crocks (ADTs; all Functor based data types)
@RobertWPearce 35
!
crocks
— Crocks (ADTs; all Functor based data types)
— Monoids
@RobertWPearce 35
!
crocks
— Crocks (ADTs; all Functor based data types)
— Monoids
— Combinators
@RobertWPearce 35
!
crocks
— Crocks (ADTs; all Functor based data types)
— Monoids
— Combinators
— Helper functions
@RobertWPearce 35
!
crocks
— Crocks (ADTs; all Functor based data types)
— Monoids
— Combinators
— Helper functions
— Logic functions
@RobertWPearce 35
!
crocks
— Crocks (ADTs; all Functor based data types)
— Monoids
— Combinators
— Helper functions
— Logic functions
— Predicate functions
@RobertWPearce 35
!
crocks
— Crocks (ADTs; all Functor based data types)
— Monoids
— Combinators
— Helper functions
— Logic functions
— Predicate functions
— Point-free functions
@RobertWPearce 35
!
crocks
— Crocks (ADTs; all Functor based data types)
— Monoids
— Combinators
— Helper functions
— Logic functions
— Predicate functions
— Point-free functions
— Transformation functions
@RobertWPearce 35
!
crocks – Maybe
@RobertWPearce 36
!
crocks – Maybe
— Sum type
@RobertWPearce 36
!
crocks – Maybe
— Sum type
— Captures disjunction
@RobertWPearce 36
!
crocks – Maybe
— Sum type
— Captures disjunction
— Good for trying something that could fail and falling
back to a default
@RobertWPearce 36
!
crocks – Maybe
— Sum type
— Captures disjunction
— Good for trying something that could fail and falling
back to a default
— Left side (fail) fixed to () (Nothing), discarding result
@RobertWPearce 36
!
crocks – Maybe
— Sum type
— Captures disjunction
— Good for trying something that could fail and falling
back to a default
— Left side (fail) fixed to () (Nothing), discarding result
— Right side (success) returns Just a, boxing the result
@RobertWPearce 36
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => [
// "<li>Crush the Rebel scum</li>",
// "<li>Mess with Texas</li>",
// "<li>Do laundry</li>"
// ]
@RobertWPearce 37
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => [
// "<li>Crush the Rebel scum</li>",
// "<li>Mess with Texas</li>",
// "<li>Do laundry</li>"
// ]
@RobertWPearce 37
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => [
// "<li>Crush the Rebel scum</li>",
// "<li>Mess with Texas</li>",
// "<li>Do laundry</li>"
// ]
@RobertWPearce 37
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => [
// "<li>Crush the Rebel scum</li>",
// "<li>Mess with Texas</li>",
// "<li>Do laundry</li>"
// ]
@RobertWPearce 37
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => [
// "<li>Crush the Rebel scum</li>",
// "<li>Mess with Texas</li>",
// "<li>Do laundry</li>"
// ]
@RobertWPearce 37
!
crocks – Maybe – Retired Vader's To-Do List
const store = {}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => TypeError: Cannot read property 'map' of undefined
@RobertWPearce 38
!
crocks – Maybe – Retired Vader's To-Do List
const store = {}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => TypeError: Cannot read property 'map' of undefined
@RobertWPearce 38
!
crocks – Maybe – Retired Vader's To-Do List
const store = {}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => TypeError: Cannot read property 'map' of undefined
@RobertWPearce 38
!
crocks – Maybe – Retired Vader's To-Do List
const store = {}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => TypeError: Cannot read property 'map' of undefined
@RobertWPearce 38
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: {
abc: { id: 'abc', title: 'Crush the Rebel scum' },
def: { id: 'def', title: 'Mess with Texas' },
ghi: { id: 'ghi', title: 'Do laundry' },
}
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => TypeError: store.todos.map is not a function
@RobertWPearce 39
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: {
abc: { id: 'abc', title: 'Crush the Rebel scum' },
def: { id: 'def', title: 'Mess with Texas' },
ghi: { id: 'ghi', title: 'Do laundry' },
}
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => TypeError: store.todos.map is not a function
@RobertWPearce 39
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: {
abc: { id: 'abc', title: 'Crush the Rebel scum' },
def: { id: 'def', title: 'Mess with Texas' },
ghi: { id: 'ghi', title: 'Do laundry' },
}
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => TypeError: store.todos.map is not a function
@RobertWPearce 39
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: {
abc: { id: 'abc', title: 'Crush the Rebel scum' },
def: { id: 'def', title: 'Mess with Texas' },
ghi: { id: 'ghi', title: 'Do laundry' },
}
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
store.todos.map(todoHtml)
// => TypeError: store.todos.map is not a function
@RobertWPearce 39
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: (String, Object) -> Array
const propArray = (key, obj) => {
if (Array.isArray(obj[key])) {
return obj[key]
}
return []
}
propArray('todos', store).map(todoHtml)
// => [ ... ]
@RobertWPearce 40
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: (String, Object) -> Array
const propArray = (key, obj) => {
if (Array.isArray(obj[key])) {
return obj[key]
}
return []
}
propArray('todos', store).map(todoHtml)
// => [ ... ]
@RobertWPearce 40
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: (String, Object) -> Array
const propArray = (key, obj) => {
if (Array.isArray(obj[key])) {
return obj[key]
}
return []
}
propArray('todos', store).map(todoHtml)
// => [ ... ]
@RobertWPearce 40
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: (String, Object) -> Array
const propArray = (key, obj) => {
if (Array.isArray(obj[key])) {
return obj[key]
}
return []
}
propArray('todos', store).map(todoHtml)
// => [ ... ]
@RobertWPearce 40
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: (String, Object) -> Array
const propArray = (key, obj) => {
if (Array.isArray(obj[key])) {
return obj[key]
}
return []
}
propArray('todos', store).map(todoHtml)
// => [ ... ]
@RobertWPearce 40
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: (String, Object) -> Array
const propArray = (key, obj) => {
if (Array.isArray(obj[key])) {
return obj[key]
}
return []
}
propArray('todos', store).map(todoHtml)
// => [ ... ]
@RobertWPearce 40
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: (String, Object) -> Array
const propArray = (key, obj) => {
if (Array.isArray(obj[key])) {
return obj[key]
}
return []
}
propArray('todos', store).map(todoHtml)
// => [ ... ]
@RobertWPearce 40
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: (String, Object) -> Array
const propArray = (key, obj) => {
if (Array.isArray(obj[key])) {
return obj[key]
}
return []
}
propArray('todos', null).map(todoHtml)
// => TypeError: Cannot read property 'todos' of null
@RobertWPearce 41
!
crocks – Maybe – Retired Vader's To-Do List
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: (String, Object) -> Array
const propArray = (key, obj) => {
if (Array.isArray(obj[key])) {
return obj[key]
}
return []
}
propArray('todos', null).map(todoHtml)
// => TypeError: Cannot read property 'todos' of null
@RobertWPearce 41
!
crocks – Maybe – Retired Vader's To-Do List
prop :: (String | Integer) -> a -> Maybe b
@RobertWPearce 42
!
crocks – Maybe – Retired Vader's To-Do List
import { prop } from 'crocks'
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
prop('todos', store)
// => Just [ ... ]
prop('todos', null)
// => Nothing
@RobertWPearce 43
!
crocks – Maybe – Retired Vader's To-Do List
import { prop } from 'crocks'
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
prop('todos', store)
// => Just [ ... ]
prop('todos', null)
// => Nothing
@RobertWPearce 43
!
crocks – Maybe – Retired Vader's To-Do List
import { prop } from 'crocks'
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
prop('todos', store)
// => Just [ ... ]
prop('todos', null)
// => Nothing
@RobertWPearce 43
!
crocks – Maybe – Retired Vader's To-Do List
import { prop } from 'crocks'
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
prop('todos', store)
// => Just [ ... ]
prop('todos', null)
// => Nothing
@RobertWPearce 43
!
crocks – Maybe – Retired Vader's To-Do List
import { prop } from 'crocks'
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
prop('todos', store)
// => Just [ ... ]
prop('todos', null)
// => Nothing
@RobertWPearce 43
!
crocks – Maybe – Retired Vader's To-Do List
import { prop } from 'crocks'
const store = {
todos: [
{ id: 'abc', title: 'Crush the Rebel scum' },
{ id: 'def', title: 'Mess with Texas' },
{ id: 'ghi', title: 'Do laundry' },
]
}
prop('todos', store)
// => Just [ ... ]
prop('todos', null)
// => Nothing
@RobertWPearce 43
!
crocks – Maybe – Retired Vader's To-Do List
chain :: Chain m => (a -> m b) -> m a -> m b
safe :: ((a -> Boolean) | Pred) -> a -> Maybe a
@RobertWPearce 44
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, prop, safe } from 'crocks'
// propArray :: String -> Object -> Array
const propArray =
compose(
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => Just [ ... ]
propArray('todos', { todos: {} })
// => Nothing
@RobertWPearce 45
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, prop, safe } from 'crocks'
// propArray :: String -> Object -> Array
const propArray =
compose(
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => Just [ ... ]
propArray('todos', { todos: {} })
// => Nothing
@RobertWPearce 45
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, prop, safe } from 'crocks'
// propArray :: String -> Object -> Array
const propArray =
compose(
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => Just [ ... ]
propArray('todos', { todos: {} })
// => Nothing
@RobertWPearce 45
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, prop, safe } from 'crocks'
// propArray :: String -> Object -> Array
const propArray =
compose(
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => Just [ ... ]
propArray('todos', { todos: {} })
// => Nothing
@RobertWPearce 45
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, prop, safe } from 'crocks'
// propArray :: String -> Object -> Array
const propArray =
compose(
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => Just [ ... ]
propArray('todos', { todos: {} })
// => Nothing
@RobertWPearce 45
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, prop, safe } from 'crocks'
// propArray :: String -> Object -> Array
const propArray =
compose(
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => Just [ ... ]
propArray('todos', { todos: {} })
// => Nothing
@RobertWPearce 45
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, prop, safe } from 'crocks'
// propArray :: String -> Object -> Array
const propArray =
compose(
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => Just [ ... ]
propArray('todos', { todos: {} })
// => Nothing
@RobertWPearce 45
!
crocks – Maybe – Retired Vader's To-Do List
option :: Maybe a ~> a -> a
@RobertWPearce 46
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => [ ... ]
propArray('todos', null)
// => []
propArray('todos', { todos: {} })
// => []
@RobertWPearce 47
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => [ ... ]
propArray('todos', null)
// => []
propArray('todos', { todos: {} })
// => []
@RobertWPearce 47
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => [ ... ]
propArray('todos', null)
// => []
propArray('todos', { todos: {} })
// => []
@RobertWPearce 47
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => [ ... ]
propArray('todos', null)
// => []
propArray('todos', { todos: {} })
// => []
@RobertWPearce 47
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => [ ... ]
propArray('todos', null)
// => []
propArray('todos', { todos: {} })
// => []
@RobertWPearce 47
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => [ ... ]
propArray('todos', null)
// => []
propArray('todos', { todos: {} })
// => []
@RobertWPearce 47
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => [ ... ]
propArray('todos', null)
// => []
propArray('todos', { todos: {} })
// => []
@RobertWPearce 47
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => [ ... ]
propArray('todos', null)
// => []
propArray('todos', { todos: {} })
// => []
@RobertWPearce 47
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store)
// => [ ... ]
propArray('todos', null)
// => []
propArray('todos', { todos: {} })
// => []
@RobertWPearce 47
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store).map(todoHtml)
// => [
// "<li>Crush the Rebel scum</li>",
// "<li>Mess with Texas</li>",
// "<li>Do laundry</li>"
// ]
@RobertWPearce 48
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store).map(todoHtml)
// => [
// "<li>Crush the Rebel scum</li>",
// "<li>Mess with Texas</li>",
// "<li>Do laundry</li>"
// ]
@RobertWPearce 48
!
crocks – Maybe – Retired Vader's To-Do List
import { chain, option, prop, safe } from 'crocks'
const store = { ... }
const todoHtml = todo =>
`<li>${todo.title}</li>`
// propArray :: String -> Object -> Array
const propArray =
compose(
option([]),
chain(safe(isArray)),
prop
)
propArray('todos', store).map(todoHtml)
// => [
// "<li>Crush the Rebel scum</li>",
// "<li>Mess with Texas</li>",
// "<li>Do laundry</li>"
// ]
@RobertWPearce 48
Take a breath
@RobertWPearce 49
Some last bits on crocks
@RobertWPearce 50
!
crocks – Async
Async e a = Rejected e | Resolved a
@RobertWPearce 51
!
crocks – Async
Async e a = Rejected e | Resolved a
— "lazy" Sum Type
@RobertWPearce 51
!
crocks – Async
Async e a = Rejected e | Resolved a
— "lazy" Sum Type
— Asynchronous control structure
@RobertWPearce 51
!
crocks – Async
Async e a = Rejected e | Resolved a
— "lazy" Sum Type
— Asynchronous control structure
— Will not run or execute until needed
@RobertWPearce 51
!
crocks – Async
Async e a = Rejected e | Resolved a
— "lazy" Sum Type
— Asynchronous control structure
— Will not run or execute until needed
— Cancellable (now with a given timeout)
@RobertWPearce 51
!
crocks – Async
Async e a = Rejected e | Resolved a
— "lazy" Sum Type
— Asynchronous control structure
— Will not run or execute until needed
— Cancellable (now with a given timeout)
— Helpers to convert Promise-returning functions and
"nodeback"-style functions to Asyncs
@RobertWPearce 51
!
crocks – Async
Async e a = Rejected e | Resolved a
— "lazy" Sum Type
— Asynchronous control structure
— Will not run or execute until needed
— Cancellable (now with a given timeout)
— Helpers to convert Promise-returning functions and
"nodeback"-style functions to Asyncs
— chain, map, bimap, coalesce and alt are examples of instance
functions
@RobertWPearce 51
!
crocks – State
State s a
@RobertWPearce 52
!
crocks – State
State s a
— "lazy", Product Type
@RobertWPearce 52
!
crocks – State
State s a
— "lazy", Product Type
— Parameterized by two types: State s and resultant a
@RobertWPearce 52
!
crocks – State
State s a
— "lazy", Product Type
— Parameterized by two types: State s and resultant a
— All instances wrap a function of s -> Pair a s
@RobertWPearce 52
!
crocks – ADTs
Arrow, Async, Const, Either, Equiv, IO, Maybe, Pair, Pred,
Reader, ReaderT, Result, Star, State, Tuple, Writer and
more not listed here.
@RobertWPearce 53
!
crocks – Monoids
@RobertWPearce 54
!
crocks – Monoids
— All, Any, Assign, Endo, First, Last, Max, Min, Prod, Sum
@RobertWPearce 54
!
crocks – Monoids
— All, Any, Assign, Endo, First, Last, Max, Min, Prod, Sum
— All work with mconcat, mreduce, mconcatMap and
mreduceMap
@RobertWPearce 54
!
crocks – Functions
@RobertWPearce 55
!
crocks – Functions
— Combinators: applyTo, flipping arguments, identity, constant,
etc.
@RobertWPearce 55
!
crocks – Functions
— Combinators: applyTo, flipping arguments, identity, constant,
etc.
— Helpers: all sorts of support functions. assign, compose, liftA2,
etc.
@RobertWPearce 55
!
crocks – Functions
— Combinators: applyTo, flipping arguments, identity, constant,
etc.
— Helpers: all sorts of support functions. assign, compose, liftA2,
etc.
— Logic: All sorts of logic based combinators. All work with
predicate functions
@RobertWPearce 55
!
crocks – Functions
— Combinators: applyTo, flipping arguments, identity, constant,
etc.
— Helpers: all sorts of support functions. assign, compose, liftA2,
etc.
— Logic: All sorts of logic based combinators. All work with
predicate functions
— Predicates: hasProp, isApplicative, isSemigroup...so many more
@RobertWPearce 55
!
crocks – Functions
— Combinators: applyTo, flipping arguments, identity, constant,
etc.
— Helpers: all sorts of support functions. assign, compose, liftA2,
etc.
— Logic: All sorts of logic based combinators. All work with
predicate functions
— Predicates: hasProp, isApplicative, isSemigroup...so many more
— Point-free: For every algebra available on both the Crocks and
Monoids, there is a function here.
@RobertWPearce 55
!
crocks – Functions
— Combinators: applyTo, flipping arguments, identity, constant,
etc.
— Helpers: all sorts of support functions. assign, compose, liftA2,
etc.
— Logic: All sorts of logic based combinators. All work with
predicate functions
— Predicates: hasProp, isApplicative, isSemigroup...so many more
— Point-free: For every algebra available on both the Crocks and
Monoids, there is a function here.
— Transformation: eitherToMaybe, maybeToAsync, resultToEither, etc
@RobertWPearce 55
JS isn't going anywhere
@RobertWPearce • Photo by Emanuele Dellepiane on Unsplash 56
FP in JS is young
But you can do it
@RobertWPearce • Photo by Rod Long on Unsplash 57
Reach for tools
like these
@RobertWPearce • Photo by Aaron Kato on Unsplash 58
FP in JS-land
Thanks!
tweeter :: @RobertWPearce
github :: rpearce
email :: me@robertwpearce.com
@RobertWPearce 59

FP in JS-Land

  • 1.
    FP in JS-Land tweeter:: @RobertWPearce github :: rpearce email :: me@robertwpearce.com @RobertWPearce 1
  • 2.
  • 3.
  • 4.
  • 5.
    Overview 1. ✅ FP-enablers 2. ✈ 30,000ft view ofFP tools 3. # ramda.js @RobertWPearce 2
  • 6.
    Overview 1. ✅ FP-enablers 2. ✈ 30,000ft view ofFP tools 3. # ramda.js 4. $ crocks.js @RobertWPearce 2
  • 7.
    Overview 1. ✅ FP-enablers 2. ✈ 30,000ft view ofFP tools 3. # ramda.js 4. $ crocks.js @RobertWPearce 3
  • 8.
    ✅ Pass functions asvalues const handleClick = e => { console.log('click event: ', e) } document.addEventListener('click', handleClick) @RobertWPearce 4
  • 9.
    ✅ Pass functions asvalues const handleClick = e => { console.log('click event: ', e) } document.addEventListener('click', handleClick) @RobertWPearce 4
  • 10.
    ✅ Pass functions asvalues const handleClick = e => { console.log('click event: ', e) } document.addEventListener('click', handleClick) @RobertWPearce 4
  • 11.
    ✅ Partial application &currying const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 const add = a => b => b + a const add3 = add(3) add3(2) // 5 @RobertWPearce 5
  • 12.
    ✅ Partial application &currying const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 const add = a => b => b + a const add3 = add(3) add3(2) // 5 @RobertWPearce 5
  • 13.
    ✅ Partial application &currying const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 const add = a => b => b + a const add3 = add(3) add3(2) // 5 @RobertWPearce 5
  • 14.
    ✅ Partial application &currying const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 const add = a => b => b + a const add3 = add(3) add3(2) // 5 @RobertWPearce 5
  • 15.
    ✅ Partial application &currying const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 const add = a => b => b + a const add3 = add(3) add3(2) // 5 @RobertWPearce 5
  • 16.
    ✅ Partial application &currying const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 const add = a => b => b + a const add3 = add(3) add3(2) // 5 @RobertWPearce 5
  • 17.
    ✅ Partial application &currying const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 const add = a => b => b + a const add3 = add(3) add3(2) // 5 @RobertWPearce 5
  • 18.
    ✅ Composition const add =a => b => b + a const add2 = add(2) const sub = a => b => b - a const sub1 = sub(1) const mult = a => b => b * a const mult10 = mult(10) mult10(sub1(add2(9))) // 100 @RobertWPearce 6
  • 19.
    ✅ Composition const add =a => b => b + a const add2 = add(2) const sub = a => b => b - a const sub1 = sub(1) const mult = a => b => b * a const mult10 = mult(10) mult10(sub1(add2(9))) // 100 @RobertWPearce 6
  • 20.
    ✅ Composition const add =a => b => b + a const add2 = add(2) const sub = a => b => b - a const sub1 = sub(1) const mult = a => b => b * a const mult10 = mult(10) mult10(sub1(add2(9))) // 100 @RobertWPearce 6
  • 21.
    ✅ Composition const add =a => b => b + a const add2 = add(2) const sub = a => b => b - a const sub1 = sub(1) const mult = a => b => b * a const mult10 = mult(10) mult10(sub1(add2(9))) // 100 @RobertWPearce 6
  • 22.
    ✅ Composition const add =a => b => b + a const add2 = add(2) const sub = a => b => b - a const sub1 = sub(1) const mult = a => b => b * a const mult10 = mult(10) mult10(sub1(add2(9))) // 100 @RobertWPearce 6
  • 23.
    ✅ map const films =[ { title: 'The Empire Strikes Back', rating: 8.8 }, { title: 'Pulp Fiction', rating: 8.9 }, { title: 'The Deer Hunter', rating: 8.2 }, { title: 'The Lion King', rating: 8.5 } ] // filmHtml :: Film -> Html const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` films.map(filmHtml) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // "<div>The Deer Hunter, <strong>8.2</strong></div>", // "<div>The Lion King, <strong>8.5</strong></div>" // ] @RobertWPearce 7
  • 24.
    ✅ map const films =[ { title: 'The Empire Strikes Back', rating: 8.8 }, { title: 'Pulp Fiction', rating: 8.9 }, { title: 'The Deer Hunter', rating: 8.2 }, { title: 'The Lion King', rating: 8.5 } ] // filmHtml :: Film -> Html const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` films.map(filmHtml) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // "<div>The Deer Hunter, <strong>8.2</strong></div>", // "<div>The Lion King, <strong>8.5</strong></div>" // ] @RobertWPearce 7
  • 25.
    ✅ map const films =[ { title: 'The Empire Strikes Back', rating: 8.8 }, { title: 'Pulp Fiction', rating: 8.9 }, { title: 'The Deer Hunter', rating: 8.2 }, { title: 'The Lion King', rating: 8.5 } ] // filmHtml :: Film -> Html const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` films.map(filmHtml) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // "<div>The Deer Hunter, <strong>8.2</strong></div>", // "<div>The Lion King, <strong>8.5</strong></div>" // ] @RobertWPearce 7
  • 26.
    ✅ map const films =[ { title: 'The Empire Strikes Back', rating: 8.8 }, { title: 'Pulp Fiction', rating: 8.9 }, { title: 'The Deer Hunter', rating: 8.2 }, { title: 'The Lion King', rating: 8.5 } ] // filmHtml :: Film -> Html const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` films.map(filmHtml) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // "<div>The Deer Hunter, <strong>8.2</strong></div>", // "<div>The Lion King, <strong>8.5</strong></div>" // ] @RobertWPearce 7
  • 27.
    ✅ map const films =[ { title: 'The Empire Strikes Back', rating: 8.8 }, { title: 'Pulp Fiction', rating: 8.9 }, { title: 'The Deer Hunter', rating: 8.2 }, { title: 'The Lion King', rating: 8.5 } ] // filmHtml :: Film -> Html const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` films.map(filmHtml) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // "<div>The Deer Hunter, <strong>8.2</strong></div>", // "<div>The Lion King, <strong>8.5</strong></div>" // ] @RobertWPearce 7
  • 28.
    ✅ filter const films =// ... // hasHighScore :: Film -> Bool const hasHighScore = film => film.rating >= 8.8 films .filter(hasHighScore) .map(filmHtml) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // ] @RobertWPearce 8
  • 29.
    ✅ filter const films =// ... // hasHighScore :: Film -> Bool const hasHighScore = film => film.rating >= 8.8 films .filter(hasHighScore) .map(filmHtml) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // ] @RobertWPearce 8
  • 30.
    ✅ filter const films =// ... // hasHighScore :: Film -> Bool const hasHighScore = film => film.rating >= 8.8 films .filter(hasHighScore) .map(filmHtml) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // ] @RobertWPearce 8
  • 31.
    ✅ filter const films =// ... // hasHighScore :: Film -> Bool const hasHighScore = film => film.rating >= 8.8 films .filter(hasHighScore) .map(filmHtml) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // ] @RobertWPearce 8
  • 32.
    ✅ filter const films =// ... // hasHighScore :: Film -> Bool const hasHighScore = film => film.rating >= 8.8 films .filter(hasHighScore) .map(filmHtml) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // ] @RobertWPearce 8
  • 33.
    ✅ reduce (fold) const films= // ... // highScoresHtml :: ([Html], Film) -> [Html] const highScoresHtml = (acc, film) => hasHighScore(film) ? acc.concat(filmHtml(film)) : acc films.reduce(highScoresHtml, []) @RobertWPearce 9
  • 34.
    ✅ reduce (fold) const films= // ... // highScoresHtml :: ([Html], Film) -> [Html] const highScoresHtml = (acc, film) => hasHighScore(film) ? acc.concat(filmHtml(film)) : acc films.reduce(highScoresHtml, []) @RobertWPearce 9
  • 35.
    ✅ reduce (fold) const films= // ... // highScoresHtml :: ([Html], Film) -> [Html] const highScoresHtml = (acc, film) => hasHighScore(film) ? acc.concat(filmHtml(film)) : acc films.reduce(highScoresHtml, []) @RobertWPearce 9
  • 36.
    Not in vanillaJS @RobertWPearce 10
  • 37.
    Not in vanillaJS — ❌ pattern matching @RobertWPearce 10
  • 38.
    Not in vanillaJS — ❌ pattern matching — ❌ compile-time type checking @RobertWPearce 10
  • 39.
    Not in vanillaJS — ❌ pattern matching — ❌ compile-time type checking — ❌ automatic function currying @RobertWPearce 10
  • 40.
    Not in vanillaJS — ❌ pattern matching — ❌ compile-time type checking — ❌ automatic function currying — ❌ easy variadic function composition @RobertWPearce 10
  • 41.
    Not in vanillaJS — ❌ pattern matching — ❌ compile-time type checking — ❌ automatic function currying — ❌ easy variadic function composition — ❌ algebraic data types @RobertWPearce 10
  • 42.
    Not in vanillaJS — ❌ pattern matching — ❌ compile-time type checking — ❌ automatic function currying — ❌ easy variadic function composition — ❌ algebraic data types — ❌ ______ (insert nice FP thing here) @RobertWPearce 10
  • 43.
    Overview 1. ✅ FP-enablers 2. ✈ 30,000! view ofFP tools 3. # ramda.js 4. $ crocks.js @RobertWPearce 11
  • 44.
    Libraries, tools &education — Fantasy Land Specification — Professor Frisby's Mostly Adequate Guide to Functional Programming — ramda — lodash/fp — immutable.js — folktale — CycleJS — RxJS — flyd — fp-ts: Functional programming in TypeScript — crocks @RobertWPearce 12
  • 45.
    Compile-time type-checking — TypeScript(type-checker & transpiler) — Flow (type-checker) @RobertWPearce 13
  • 46.
    Tools with courseson egghead.io: — CycleJS — RxJS — immutable.js — TypeScript — Flow — ramda — crocks @RobertWPearce 14
  • 47.
    Overview 1. ✅ FP-enablers 2. ✈ 30,000ft view ofFP tools 3. # ramda.js 4. $ crocks.js @RobertWPearce 15
  • 48.
    "Practical, functional JavaScript." —ramdajs.com @RobertWPearce • Photo by Kirill Zakharov on Unsplash 16
  • 49.
    ! ramda @RobertWPearce • Photoby George Hiles on Unsplash 17
  • 50.
    ! ramda — easier tocreate functional pipelines @RobertWPearce • Photo by George Hiles on Unsplash 17
  • 51.
    ! ramda — easier tocreate functional pipelines — immutability @RobertWPearce • Photo by George Hiles on Unsplash 17
  • 52.
    ! ramda — easier tocreate functional pipelines — immutability — side-effect free code @RobertWPearce • Photo by George Hiles on Unsplash 17
  • 53.
    ! ramda — easier tocreate functional pipelines — immutability — side-effect free code — all functions curried @RobertWPearce • Photo by George Hiles on Unsplash 17
  • 54.
    ! ramda — easier tocreate functional pipelines — immutability — side-effect free code — all functions curried — provide data last @RobertWPearce • Photo by George Hiles on Unsplash 17
  • 55.
  • 56.
    refactor with ! ramda // mult10(sub1(add2(9)))// 100 import { compose } from 'ramda' @RobertWPearce 19
  • 57.
    refactor with ! ramda // mult10(sub1(add2(9)))// 100 import { compose } from 'ramda' compose(mult10, sub1, add2)(9) // 100 @RobertWPearce 20
  • 58.
    refactor with ! ramda // mult10(sub1(add2(9)))// 100 import { compose } from 'ramda' // compose(mult10, sub1, add2)(9) // 100 const doMaths = compose(mult10, sub1, add2) doMaths(9) // 100 @RobertWPearce 21
  • 59.
    refactor with ! ramda #2 films.filter(hasHighScore).map(filmHtml) //=> [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // ] @RobertWPearce 22
  • 60.
    refactor with ! ramda #2 //films.filter(hasHighScore).map(filmHtml) import { compose, filter, map } from 'ramda' @RobertWPearce 23
  • 61.
    refactor with ! ramda #2 //films.filter(hasHighScore).map(filmHtml) import { compose, filter, map } from 'ramda' compose(map(filmHtml), filter(hasHighScore)) @RobertWPearce 24
  • 62.
    refactor with ! ramda #2 //films.filter(hasHighScore).map(filmHtml) import { compose, filter, map } from 'ramda' // output :: [Film] -> [Html] const output = compose(map(filmHtml), filter(hasHighScore)) @RobertWPearce 25
  • 63.
    refactor with ! ramda #2 //films.filter(hasHighScore).map(filmHtml) import { compose, filter, map } from 'ramda' // output :: [Film] -> [Html] const output = compose(map(filmHtml), filter(hasHighScore)) output(films) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // ] @RobertWPearce 26
  • 64.
    refactor with ! ramda #2 //films.filter(hasHighScore).map(filmHtml) import { compose, filter, map } from 'ramda' // output :: [Film] -> [Html] const output = compose(map(filmHtml), filter(hasHighScore)) output(films) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // ] @RobertWPearce 26
  • 65.
    refactor with ! ramda #2 //films.filter(hasHighScore).map(filmHtml) import { compose, filter, map } from 'ramda' // filmsToHtml :: [Film] -> [Html] const filmsToHtml = map(filmHtml) // highScores :: [Film] -> [Film] const highScores = filter(hasHighScore) // output :: [Film] -> [Html] const output = compose(filmsToHtml, highScores) output(films) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>", // ] @RobertWPearce 27
  • 66.
    ! ramda example: goodbyeif + else if & switch // getComponentType :: Props -> <Component /> const getComponentType = props => { if (props.field) { return Field } else if (props.pill) { return Pill } else if (props.pillWithLabel) { return PillWithLabel } else if (props.withIcon) { return ValueWithIcon } else if (props.withLabel) { return ValueWithLabel } else { return Value } } // ... <MyPolymorphicComponent pillWithLabel {...otherProps} /> // ... const Component = getComponentType(props) return <Component {...props} /> @RobertWPearce 28
  • 67.
    ! ramda example: goodbyeif + else if & switch // getComponentType :: Props -> <Component /> const getComponentType = props => { if (props.field) { return Field } else if (props.pill) { return Pill } else if (props.pillWithLabel) { return PillWithLabel } else if (props.withIcon) { return ValueWithIcon } else if (props.withLabel) { return ValueWithLabel } else { return Value } } // ... <MyPolymorphicComponent pillWithLabel {...otherProps} /> // ... const Component = getComponentType(props) return <Component {...props} /> @RobertWPearce 28
  • 68.
    ! ramda example: goodbyeif + else if & switch // getComponentType :: Props -> <Component /> const getComponentType = props => { if (props.field) { return Field } else if (props.pill) { return Pill } else if (props.pillWithLabel) { return PillWithLabel } else if (props.withIcon) { return ValueWithIcon } else if (props.withLabel) { return ValueWithLabel } else { return Value } } // ... <MyPolymorphicComponent pillWithLabel {...otherProps} /> // ... const Component = getComponentType(props) return <Component {...props} /> @RobertWPearce 28
  • 69.
    ! ramda example: goodbyeif + else if & switch // getComponentType :: Props -> <Component /> const getComponentType = props => { if (props.field) { return Field } else if (props.pill) { return Pill } else if (props.pillWithLabel) { return PillWithLabel } else if (props.withIcon) { return ValueWithIcon } else if (props.withLabel) { return ValueWithLabel } else { return Value } } // ... <MyPolymorphicComponent pillWithLabel {...otherProps} /> // ... const Component = getComponentType(props) return <Component {...props} /> @RobertWPearce 28
  • 70.
    ! ramda example: goodbyeif + else if & switch const isType = propEq(__, true) // getComponentType :: Props -> <Component /> const getComponentType = cond([ [ isType('field') , always(Field) ], [ isType('pill') , always(Pill) ], [ isType('pillWithLabel'), always(PillWithLabel) ], [ isType('withIcon') , always(ValueWithIcon) ], [ isType('withLabel') , always(ValueWithLabel) ], [ T , always(Value) ], ]) // ... <MyPolymorphicComponent pillWithLabel {...otherProps} /> // ... const Component = getComponentType(props) return <Component {...props} /> @RobertWPearce 29
  • 71.
    ! ramda example: convertingmethod APIs document.querySelectorAll('div').length // 76 @RobertWPearce 30
  • 72.
    ! ramda example: convertingmethod APIs import { compose, invoker, length } from 'ramda' const querySelectorAll = invoker(1, 'querySelectorAll') const countDivs = compose(length, querySelectorAll('div')) countDivs(document) // 76 @RobertWPearce 31
  • 73.
    ! ramda example: convertingmethod APIs import { compose, invoker, length } from 'ramda' const querySelectorAll = invoker(1, 'querySelectorAll') const countDivs = compose(length, querySelectorAll('div')) countDivs(document) // 76 @RobertWPearce 31
  • 74.
    ! ramda example: convertingmethod APIs import { compose, invoker, length } from 'ramda' const querySelectorAll = invoker(1, 'querySelectorAll') const countDivs = compose(length, querySelectorAll('div')) countDivs(document) // 76 @RobertWPearce 31
  • 75.
    ! ramda example: convertingmethod APIs import { compose, invoker, length } from 'ramda' const querySelectorAll = invoker(1, 'querySelectorAll') const countDivs = compose(length, querySelectorAll('div')) countDivs(document) // 76 @RobertWPearce 31
  • 76.
    ! ramda example: convertingmethod APIs import { compose, invoker, length } from 'ramda' const querySelectorAll = invoker(1, 'querySelectorAll') const countDivs = compose(length, querySelectorAll('div')) countDivs(document) // 76 @RobertWPearce 31
  • 77.
    ! ramda has heapsof helpers — ap — chain — concat — compose & composeK (Kleisli) & composeP (Promises) & pipe family — curry & curryN — fromPairs & toPairs — either & both — over & evolve & lens & set & view — invoker — juxt — map & filter & reduce — merge & mergeDeepLeft & mergeDeepRight — omit — pick — AND SO MANY MORE @RobertWPearce 32
  • 78.
    Overview 1. ✅ FP-enablers 2. ✈ 30,000ft view ofFP tools 3. # ramda.js 4. $ crocks.js @RobertWPearce 33
  • 79.
    A collection ofwell known Algebraic Data Types for your utter enjoyment. — https://evilsoft.github.io/crocks @RobertWPearce • Photo by Cel Lisboa on Unsplash 34
  • 80.
  • 81.
    ! crocks — Crocks (ADTs;all Functor based data types) @RobertWPearce 35
  • 82.
    ! crocks — Crocks (ADTs;all Functor based data types) — Monoids @RobertWPearce 35
  • 83.
    ! crocks — Crocks (ADTs;all Functor based data types) — Monoids — Combinators @RobertWPearce 35
  • 84.
    ! crocks — Crocks (ADTs;all Functor based data types) — Monoids — Combinators — Helper functions @RobertWPearce 35
  • 85.
    ! crocks — Crocks (ADTs;all Functor based data types) — Monoids — Combinators — Helper functions — Logic functions @RobertWPearce 35
  • 86.
    ! crocks — Crocks (ADTs;all Functor based data types) — Monoids — Combinators — Helper functions — Logic functions — Predicate functions @RobertWPearce 35
  • 87.
    ! crocks — Crocks (ADTs;all Functor based data types) — Monoids — Combinators — Helper functions — Logic functions — Predicate functions — Point-free functions @RobertWPearce 35
  • 88.
    ! crocks — Crocks (ADTs;all Functor based data types) — Monoids — Combinators — Helper functions — Logic functions — Predicate functions — Point-free functions — Transformation functions @RobertWPearce 35
  • 89.
  • 90.
    ! crocks – Maybe —Sum type @RobertWPearce 36
  • 91.
    ! crocks – Maybe —Sum type — Captures disjunction @RobertWPearce 36
  • 92.
    ! crocks – Maybe —Sum type — Captures disjunction — Good for trying something that could fail and falling back to a default @RobertWPearce 36
  • 93.
    ! crocks – Maybe —Sum type — Captures disjunction — Good for trying something that could fail and falling back to a default — Left side (fail) fixed to () (Nothing), discarding result @RobertWPearce 36
  • 94.
    ! crocks – Maybe —Sum type — Captures disjunction — Good for trying something that could fail and falling back to a default — Left side (fail) fixed to () (Nothing), discarding result — Right side (success) returns Just a, boxing the result @RobertWPearce 36
  • 95.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => [ // "<li>Crush the Rebel scum</li>", // "<li>Mess with Texas</li>", // "<li>Do laundry</li>" // ] @RobertWPearce 37
  • 96.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => [ // "<li>Crush the Rebel scum</li>", // "<li>Mess with Texas</li>", // "<li>Do laundry</li>" // ] @RobertWPearce 37
  • 97.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => [ // "<li>Crush the Rebel scum</li>", // "<li>Mess with Texas</li>", // "<li>Do laundry</li>" // ] @RobertWPearce 37
  • 98.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => [ // "<li>Crush the Rebel scum</li>", // "<li>Mess with Texas</li>", // "<li>Do laundry</li>" // ] @RobertWPearce 37
  • 99.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => [ // "<li>Crush the Rebel scum</li>", // "<li>Mess with Texas</li>", // "<li>Do laundry</li>" // ] @RobertWPearce 37
  • 100.
    ! crocks – Maybe– Retired Vader's To-Do List const store = {} const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => TypeError: Cannot read property 'map' of undefined @RobertWPearce 38
  • 101.
    ! crocks – Maybe– Retired Vader's To-Do List const store = {} const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => TypeError: Cannot read property 'map' of undefined @RobertWPearce 38
  • 102.
    ! crocks – Maybe– Retired Vader's To-Do List const store = {} const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => TypeError: Cannot read property 'map' of undefined @RobertWPearce 38
  • 103.
    ! crocks – Maybe– Retired Vader's To-Do List const store = {} const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => TypeError: Cannot read property 'map' of undefined @RobertWPearce 38
  • 104.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: { abc: { id: 'abc', title: 'Crush the Rebel scum' }, def: { id: 'def', title: 'Mess with Texas' }, ghi: { id: 'ghi', title: 'Do laundry' }, } } const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => TypeError: store.todos.map is not a function @RobertWPearce 39
  • 105.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: { abc: { id: 'abc', title: 'Crush the Rebel scum' }, def: { id: 'def', title: 'Mess with Texas' }, ghi: { id: 'ghi', title: 'Do laundry' }, } } const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => TypeError: store.todos.map is not a function @RobertWPearce 39
  • 106.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: { abc: { id: 'abc', title: 'Crush the Rebel scum' }, def: { id: 'def', title: 'Mess with Texas' }, ghi: { id: 'ghi', title: 'Do laundry' }, } } const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => TypeError: store.todos.map is not a function @RobertWPearce 39
  • 107.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: { abc: { id: 'abc', title: 'Crush the Rebel scum' }, def: { id: 'def', title: 'Mess with Texas' }, ghi: { id: 'ghi', title: 'Do laundry' }, } } const todoHtml = todo => `<li>${todo.title}</li>` store.todos.map(todoHtml) // => TypeError: store.todos.map is not a function @RobertWPearce 39
  • 108.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: (String, Object) -> Array const propArray = (key, obj) => { if (Array.isArray(obj[key])) { return obj[key] } return [] } propArray('todos', store).map(todoHtml) // => [ ... ] @RobertWPearce 40
  • 109.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: (String, Object) -> Array const propArray = (key, obj) => { if (Array.isArray(obj[key])) { return obj[key] } return [] } propArray('todos', store).map(todoHtml) // => [ ... ] @RobertWPearce 40
  • 110.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: (String, Object) -> Array const propArray = (key, obj) => { if (Array.isArray(obj[key])) { return obj[key] } return [] } propArray('todos', store).map(todoHtml) // => [ ... ] @RobertWPearce 40
  • 111.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: (String, Object) -> Array const propArray = (key, obj) => { if (Array.isArray(obj[key])) { return obj[key] } return [] } propArray('todos', store).map(todoHtml) // => [ ... ] @RobertWPearce 40
  • 112.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: (String, Object) -> Array const propArray = (key, obj) => { if (Array.isArray(obj[key])) { return obj[key] } return [] } propArray('todos', store).map(todoHtml) // => [ ... ] @RobertWPearce 40
  • 113.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: (String, Object) -> Array const propArray = (key, obj) => { if (Array.isArray(obj[key])) { return obj[key] } return [] } propArray('todos', store).map(todoHtml) // => [ ... ] @RobertWPearce 40
  • 114.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: (String, Object) -> Array const propArray = (key, obj) => { if (Array.isArray(obj[key])) { return obj[key] } return [] } propArray('todos', store).map(todoHtml) // => [ ... ] @RobertWPearce 40
  • 115.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: (String, Object) -> Array const propArray = (key, obj) => { if (Array.isArray(obj[key])) { return obj[key] } return [] } propArray('todos', null).map(todoHtml) // => TypeError: Cannot read property 'todos' of null @RobertWPearce 41
  • 116.
    ! crocks – Maybe– Retired Vader's To-Do List const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: (String, Object) -> Array const propArray = (key, obj) => { if (Array.isArray(obj[key])) { return obj[key] } return [] } propArray('todos', null).map(todoHtml) // => TypeError: Cannot read property 'todos' of null @RobertWPearce 41
  • 117.
    ! crocks – Maybe– Retired Vader's To-Do List prop :: (String | Integer) -> a -> Maybe b @RobertWPearce 42
  • 118.
    ! crocks – Maybe– Retired Vader's To-Do List import { prop } from 'crocks' const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } prop('todos', store) // => Just [ ... ] prop('todos', null) // => Nothing @RobertWPearce 43
  • 119.
    ! crocks – Maybe– Retired Vader's To-Do List import { prop } from 'crocks' const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } prop('todos', store) // => Just [ ... ] prop('todos', null) // => Nothing @RobertWPearce 43
  • 120.
    ! crocks – Maybe– Retired Vader's To-Do List import { prop } from 'crocks' const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } prop('todos', store) // => Just [ ... ] prop('todos', null) // => Nothing @RobertWPearce 43
  • 121.
    ! crocks – Maybe– Retired Vader's To-Do List import { prop } from 'crocks' const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } prop('todos', store) // => Just [ ... ] prop('todos', null) // => Nothing @RobertWPearce 43
  • 122.
    ! crocks – Maybe– Retired Vader's To-Do List import { prop } from 'crocks' const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } prop('todos', store) // => Just [ ... ] prop('todos', null) // => Nothing @RobertWPearce 43
  • 123.
    ! crocks – Maybe– Retired Vader's To-Do List import { prop } from 'crocks' const store = { todos: [ { id: 'abc', title: 'Crush the Rebel scum' }, { id: 'def', title: 'Mess with Texas' }, { id: 'ghi', title: 'Do laundry' }, ] } prop('todos', store) // => Just [ ... ] prop('todos', null) // => Nothing @RobertWPearce 43
  • 124.
    ! crocks – Maybe– Retired Vader's To-Do List chain :: Chain m => (a -> m b) -> m a -> m b safe :: ((a -> Boolean) | Pred) -> a -> Maybe a @RobertWPearce 44
  • 125.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, prop, safe } from 'crocks' // propArray :: String -> Object -> Array const propArray = compose( chain(safe(isArray)), prop ) propArray('todos', store) // => Just [ ... ] propArray('todos', { todos: {} }) // => Nothing @RobertWPearce 45
  • 126.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, prop, safe } from 'crocks' // propArray :: String -> Object -> Array const propArray = compose( chain(safe(isArray)), prop ) propArray('todos', store) // => Just [ ... ] propArray('todos', { todos: {} }) // => Nothing @RobertWPearce 45
  • 127.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, prop, safe } from 'crocks' // propArray :: String -> Object -> Array const propArray = compose( chain(safe(isArray)), prop ) propArray('todos', store) // => Just [ ... ] propArray('todos', { todos: {} }) // => Nothing @RobertWPearce 45
  • 128.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, prop, safe } from 'crocks' // propArray :: String -> Object -> Array const propArray = compose( chain(safe(isArray)), prop ) propArray('todos', store) // => Just [ ... ] propArray('todos', { todos: {} }) // => Nothing @RobertWPearce 45
  • 129.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, prop, safe } from 'crocks' // propArray :: String -> Object -> Array const propArray = compose( chain(safe(isArray)), prop ) propArray('todos', store) // => Just [ ... ] propArray('todos', { todos: {} }) // => Nothing @RobertWPearce 45
  • 130.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, prop, safe } from 'crocks' // propArray :: String -> Object -> Array const propArray = compose( chain(safe(isArray)), prop ) propArray('todos', store) // => Just [ ... ] propArray('todos', { todos: {} }) // => Nothing @RobertWPearce 45
  • 131.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, prop, safe } from 'crocks' // propArray :: String -> Object -> Array const propArray = compose( chain(safe(isArray)), prop ) propArray('todos', store) // => Just [ ... ] propArray('todos', { todos: {} }) // => Nothing @RobertWPearce 45
  • 132.
    ! crocks – Maybe– Retired Vader's To-Do List option :: Maybe a ~> a -> a @RobertWPearce 46
  • 133.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store) // => [ ... ] propArray('todos', null) // => [] propArray('todos', { todos: {} }) // => [] @RobertWPearce 47
  • 134.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store) // => [ ... ] propArray('todos', null) // => [] propArray('todos', { todos: {} }) // => [] @RobertWPearce 47
  • 135.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store) // => [ ... ] propArray('todos', null) // => [] propArray('todos', { todos: {} }) // => [] @RobertWPearce 47
  • 136.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store) // => [ ... ] propArray('todos', null) // => [] propArray('todos', { todos: {} }) // => [] @RobertWPearce 47
  • 137.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store) // => [ ... ] propArray('todos', null) // => [] propArray('todos', { todos: {} }) // => [] @RobertWPearce 47
  • 138.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store) // => [ ... ] propArray('todos', null) // => [] propArray('todos', { todos: {} }) // => [] @RobertWPearce 47
  • 139.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store) // => [ ... ] propArray('todos', null) // => [] propArray('todos', { todos: {} }) // => [] @RobertWPearce 47
  • 140.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store) // => [ ... ] propArray('todos', null) // => [] propArray('todos', { todos: {} }) // => [] @RobertWPearce 47
  • 141.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store) // => [ ... ] propArray('todos', null) // => [] propArray('todos', { todos: {} }) // => [] @RobertWPearce 47
  • 142.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store).map(todoHtml) // => [ // "<li>Crush the Rebel scum</li>", // "<li>Mess with Texas</li>", // "<li>Do laundry</li>" // ] @RobertWPearce 48
  • 143.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store).map(todoHtml) // => [ // "<li>Crush the Rebel scum</li>", // "<li>Mess with Texas</li>", // "<li>Do laundry</li>" // ] @RobertWPearce 48
  • 144.
    ! crocks – Maybe– Retired Vader's To-Do List import { chain, option, prop, safe } from 'crocks' const store = { ... } const todoHtml = todo => `<li>${todo.title}</li>` // propArray :: String -> Object -> Array const propArray = compose( option([]), chain(safe(isArray)), prop ) propArray('todos', store).map(todoHtml) // => [ // "<li>Crush the Rebel scum</li>", // "<li>Mess with Texas</li>", // "<li>Do laundry</li>" // ] @RobertWPearce 48
  • 145.
  • 146.
    Some last bitson crocks @RobertWPearce 50
  • 147.
    ! crocks – Async Asynce a = Rejected e | Resolved a @RobertWPearce 51
  • 148.
    ! crocks – Async Asynce a = Rejected e | Resolved a — "lazy" Sum Type @RobertWPearce 51
  • 149.
    ! crocks – Async Asynce a = Rejected e | Resolved a — "lazy" Sum Type — Asynchronous control structure @RobertWPearce 51
  • 150.
    ! crocks – Async Asynce a = Rejected e | Resolved a — "lazy" Sum Type — Asynchronous control structure — Will not run or execute until needed @RobertWPearce 51
  • 151.
    ! crocks – Async Asynce a = Rejected e | Resolved a — "lazy" Sum Type — Asynchronous control structure — Will not run or execute until needed — Cancellable (now with a given timeout) @RobertWPearce 51
  • 152.
    ! crocks – Async Asynce a = Rejected e | Resolved a — "lazy" Sum Type — Asynchronous control structure — Will not run or execute until needed — Cancellable (now with a given timeout) — Helpers to convert Promise-returning functions and "nodeback"-style functions to Asyncs @RobertWPearce 51
  • 153.
    ! crocks – Async Asynce a = Rejected e | Resolved a — "lazy" Sum Type — Asynchronous control structure — Will not run or execute until needed — Cancellable (now with a given timeout) — Helpers to convert Promise-returning functions and "nodeback"-style functions to Asyncs — chain, map, bimap, coalesce and alt are examples of instance functions @RobertWPearce 51
  • 154.
    ! crocks – State States a @RobertWPearce 52
  • 155.
    ! crocks – State States a — "lazy", Product Type @RobertWPearce 52
  • 156.
    ! crocks – State States a — "lazy", Product Type — Parameterized by two types: State s and resultant a @RobertWPearce 52
  • 157.
    ! crocks – State States a — "lazy", Product Type — Parameterized by two types: State s and resultant a — All instances wrap a function of s -> Pair a s @RobertWPearce 52
  • 158.
    ! crocks – ADTs Arrow,Async, Const, Either, Equiv, IO, Maybe, Pair, Pred, Reader, ReaderT, Result, Star, State, Tuple, Writer and more not listed here. @RobertWPearce 53
  • 159.
  • 160.
    ! crocks – Monoids —All, Any, Assign, Endo, First, Last, Max, Min, Prod, Sum @RobertWPearce 54
  • 161.
    ! crocks – Monoids —All, Any, Assign, Endo, First, Last, Max, Min, Prod, Sum — All work with mconcat, mreduce, mconcatMap and mreduceMap @RobertWPearce 54
  • 162.
  • 163.
    ! crocks – Functions —Combinators: applyTo, flipping arguments, identity, constant, etc. @RobertWPearce 55
  • 164.
    ! crocks – Functions —Combinators: applyTo, flipping arguments, identity, constant, etc. — Helpers: all sorts of support functions. assign, compose, liftA2, etc. @RobertWPearce 55
  • 165.
    ! crocks – Functions —Combinators: applyTo, flipping arguments, identity, constant, etc. — Helpers: all sorts of support functions. assign, compose, liftA2, etc. — Logic: All sorts of logic based combinators. All work with predicate functions @RobertWPearce 55
  • 166.
    ! crocks – Functions —Combinators: applyTo, flipping arguments, identity, constant, etc. — Helpers: all sorts of support functions. assign, compose, liftA2, etc. — Logic: All sorts of logic based combinators. All work with predicate functions — Predicates: hasProp, isApplicative, isSemigroup...so many more @RobertWPearce 55
  • 167.
    ! crocks – Functions —Combinators: applyTo, flipping arguments, identity, constant, etc. — Helpers: all sorts of support functions. assign, compose, liftA2, etc. — Logic: All sorts of logic based combinators. All work with predicate functions — Predicates: hasProp, isApplicative, isSemigroup...so many more — Point-free: For every algebra available on both the Crocks and Monoids, there is a function here. @RobertWPearce 55
  • 168.
    ! crocks – Functions —Combinators: applyTo, flipping arguments, identity, constant, etc. — Helpers: all sorts of support functions. assign, compose, liftA2, etc. — Logic: All sorts of logic based combinators. All work with predicate functions — Predicates: hasProp, isApplicative, isSemigroup...so many more — Point-free: For every algebra available on both the Crocks and Monoids, there is a function here. — Transformation: eitherToMaybe, maybeToAsync, resultToEither, etc @RobertWPearce 55
  • 169.
    JS isn't goinganywhere @RobertWPearce • Photo by Emanuele Dellepiane on Unsplash 56
  • 170.
    FP in JSis young But you can do it @RobertWPearce • Photo by Rod Long on Unsplash 57
  • 171.
    Reach for tools likethese @RobertWPearce • Photo by Aaron Kato on Unsplash 58
  • 172.
    FP in JS-land Thanks! tweeter:: @RobertWPearce github :: rpearce email :: me@robertwpearce.com @RobertWPearce 59