SlideShare a Scribd company logo
A Path to Point-Free JS
web :: rwp.im
github :: rpearce
email :: me@robertwpearce.com
tweeter :: @RobertWPearce
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 1
about me
Hi, I'm Robert
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
about me
Hi, I'm Robert
• My kiwi wife & I have recently moved back from New Zealand
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
about me
Hi, I'm Robert
• My kiwi wife & I have recently moved back from New Zealand
• Started out at Jack Russell Software w/ Tom Wilson in 2011
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
about me
Hi, I'm Robert
• My kiwi wife & I have recently moved back from New Zealand
• Started out at Jack Russell Software w/ Tom Wilson in 2011
• Currently with Articulate, the e-learning software company
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
about me
Hi, I'm Robert
• My kiwi wife & I have recently moved back from New Zealand
• Started out at Jack Russell Software w/ Tom Wilson in 2011
• Currently with Articulate, the e-learning software company
• Work with FP in JS all day
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
about me
Hi, I'm Robert
• My kiwi wife & I have recently moved back from New Zealand
• Started out at Jack Russell Software w/ Tom Wilson in 2011
• Currently with Articulate, the e-learning software company
• Work with FP in JS all day
• Learn Haskell (λ) and other FP in what little free time I have
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
about me
Hi, I'm Robert
• My kiwi wife & I have recently moved back from New Zealand
• Started out at Jack Russell Software w/ Tom Wilson in 2011
• Currently with Articulate, the e-learning software company
• Work with FP in JS all day
• Learn Haskell (λ) and other FP in what little free time I have
• Pretend to know what I'm doing
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
Question:
what are we going to do?
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 3
Answer:
ultimately attempt to simplify this code
// bestFilmsHtml :: [Film] -> [Html]
const bestFilmsHtml = films => {
const bestFilms = films.filter(film => film.rating >= 8.8)
const filmsHtml = bestFilms.map(film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
)
return filmsHtml
}
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 4
Answer:
which takes this data...
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 }
]
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 5
Answer:
...and returns this
[
"<div>The Empire Strikes Back, <strong>8.8</strong></div>",
"<div>Pulp Fiction, <strong>8.9</strong></div>"
]
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 6
Question:
how are we going to do that?
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 7
Answer:
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 8
Answer:
•
!
currying!
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 8
Answer:
•
!
currying!
•
"
composition!
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 8
Answer:
•
!
currying!
•
"
composition!
•
# $
eta-conversion!
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 8
Answer:
•
!
currying!
•
"
composition!
•
# $
eta-conversion!
•
%
ramda.js!
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 8
Answer:
•
!
currying!
•
"
composition!
•
# $
eta-conversion!
•
%
ramda.js!
• ...this is our Friday night
✨
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 8
Question:
what does point-free mean?
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 9
Tacit programming, also called point-free
style, is a programming paradigm in which
function definitions do not identify the
arguments (or "points") on which they
operate. Instead the definitions merely
compose other functions, among which are
combinators that manipulate the arguments.
— https://en.wikipedia.org/wiki/Tacit_programming
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 10
Combinatory logic is a notation to eliminate the
need for quantified variables in mathematical logic
[...] based on combinators which were introduced by
Schönfinkel in 1920 with the idea of providing an
analogous way to build up functions - and to remove
any mention of variables - particularly in predicate
logic. A combinator is a higher-order function that
uses only function application and earlier defined
combinators to define a result from its arguments.
— https://en.wikipedia.org/wiki/Combinatory_logic
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 11
...there are other opinions
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 12
The lack of argument naming gives
point-free style a reputation of being
unnecessarily obscure, hence the
epithet "pointless style".
— https://en.wikipedia.org/wiki/Tacit_programming
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 13
some may disagree
"Point-Free or Die: Tacit Programming in Haskell and Beyond"
https://www.youtube.com/watch?v=seVSlKazsNk
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 14
Question:
what does it look like?
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 15
(do we care about the implementation?)
(say yes)
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 }
]
bestFilmsHtml(films)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>"
// ]
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 16
(do we care about the implementation?)
(say yes)
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 }
]
bestFilmsHtml(films)
// => [
// "<div>The Empire Strikes Back, <strong>8.8</strong></div>",
// "<div>Pulp Fiction, <strong>8.9</strong></div>"
// ]
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 16
bestFilmsHtml
// bestFilmsHtml :: [Film] -> [Html]
const bestFilmsHtml =
compose(filmsHtml, onlyHighRatings)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 17
onlyHighRatings
// onlyHighRatings :: [Film] -> [Film]
const onlyHighRatings =
filter(hasHighRating)
// bestFilmsHtml :: [Film] -> [Html]
const bestFilmsHtml =
compose(filmsHtml, onlyHighRatings)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 18
hasHighRating
// hasHighRating :: Film -> Bool
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
// onlyHighRatings :: [Film] -> [Film]
const onlyHighRatings =
filter(hasHighRating)
// bestFilmsHtml :: [Film] -> [Html]
const bestFilmsHtml =
compose(filmsHtml, onlyHighRatings)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 19
filmsHtml
// filmsHtml :: [Film] -> [Html]
const filmsHtml =
map(filmHtml)
// hasHighRating :: Film -> Bool
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
// onlyHighRatings :: [Film] -> [Film]
const onlyHighRatings =
filter(hasHighRating)
// bestFilmsHtml :: [Film] -> [Html]
const bestFilmsHtml =
compose(filmsHtml, onlyHighRatings)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 20
filmHtml (notice any difference?)
// filmHtml :: Film -> Html
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
// filmsHtml :: [Film] -> [Html]
const filmsHtml =
map(filmHtml)
// hasHighRating :: Film -> Bool
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
// onlyHighRatings :: [Film] -> [Film]
const onlyHighRatings =
filter(hasHighRating)
// bestFilmsHtml :: [Film] -> [Html]
const bestFilmsHtml =
compose(filmsHtml, onlyHighRatings)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 21
filmHtml (notice any difference?)
// filmHtml :: Film -> Html
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
// filmsHtml :: [Film] -> [Html]
const filmsHtml =
map(filmHtml)
// hasHighRating :: Film -> Bool
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
// onlyHighRatings :: [Film] -> [Film]
const onlyHighRatings =
filter(hasHighRating)
// bestFilmsHtml :: [Film] -> [Html]
const bestFilmsHtml =
compose(filmsHtml, onlyHighRatings)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 21
!
buckle up
it's time for prerequisites
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 22
currying
!
and partial application
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
add2(5) // 7
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
add3(4) // 7
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
currying
!
and partial application
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
add2(5) // 7
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
add3(4) // 7
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
currying
!
and partial application
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
add2(5) // 7
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
add3(4) // 7
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
currying
!
and partial application
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
add2(5) // 7
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
add3(4) // 7
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
currying
!
and partial application
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
add2(5) // 7
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
add3(4) // 7
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
currying
!
and partial application
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
add2(5) // 7
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
add3(4) // 7
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
currying
!
and partial application
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
add2(5) // 7
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
add3(4) // 7
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
currying
!
and partial application
const add = (a, b) => b + a
const add2 = add.bind(null, 2)
add2(3) // 5
add2(5) // 7
const add = a => b => b + a
const add3 = add(3)
add3(2) // 5
add3(4) // 7
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
In mathematics and computer
science, currying is the technique of
translating the evaluation of a function
that takes multiple arguments into
evaluating a sequence of functions,
each with a single argument.
— https://en.wikipedia.org/wiki/Currying
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 24
currying
!
and partial application
const add = a => b => b + a
// is equivalent to
function add(a) {
return function(b) {
return b + a
}
}
// but...
add(2)(3) // 5
add(2, 3) // function add() ... ( °□°
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
currying
!
and partial application
const add = a => b => b + a
// is equivalent to
function add(a) {
return function(b) {
return b + a
}
}
// but...
add(2)(3) // 5
add(2, 3) // function add() ... ( °□°
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
currying
!
and partial application
const add = a => b => b + a
// is equivalent to
function add(a) {
return function(b) {
return b + a
}
}
// but...
add(2)(3) // 5
add(2, 3) // function add() ... ( °□°
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
currying
!
and partial application
const add = a => b => b + a
// is equivalent to
function add(a) {
return function(b) {
return b + a
}
}
// but...
add(2)(3) // 5
add(2, 3) // function add() ... ( °□°
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
currying
!
and partial application
const add = a => b => b + a
// is equivalent to
function add(a) {
return function(b) {
return b + a
}
}
// but...
add(2)(3) // 5
add(2, 3) // function add() ... ( °□°
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
currying
!
and partial application
const add = a => b => b + a
// is equivalent to
function add(a) {
return function(b) {
return b + a
}
}
// but...
add(2)(3) // 5
add(2, 3) // function add() ... ( °□°
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
autocurrying
// add.js
import { curry } from 'ramda'
const add = (a, b) => b + a
export default curry(add)
// some other file
import add from './add'
add(2, 3) // 5
add(2)(3) // 5
add()(2)(3) // 5
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
autocurrying
// add.js
import { curry } from 'ramda'
const add = (a, b) => b + a
export default curry(add)
// some other file
import add from './add'
add(2, 3) // 5
add(2)(3) // 5
add()(2)(3) // 5
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
autocurrying
// add.js
import { curry } from 'ramda'
const add = (a, b) => b + a
export default curry(add)
// some other file
import add from './add'
add(2, 3) // 5
add(2)(3) // 5
add()(2)(3) // 5
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
autocurrying
// add.js
import { curry } from 'ramda'
const add = (a, b) => b + a
export default curry(add)
// some other file
import add from './add'
add(2, 3) // 5
add(2)(3) // 5
add()(2)(3) // 5
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
autocurrying
// add.js
import { curry } from 'ramda'
const add = (a, b) => b + a
export default curry(add)
// some other file
import add from './add'
add(2, 3) // 5
add(2)(3) // 5
add()(2)(3) // 5
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
autocurrying
// add.js
import { curry } from 'ramda'
const add = (a, b) => b + a
export default curry(add)
// some other file
import add from './add'
add(2, 3) // 5
add(2)(3) // 5
add()(2)(3) // 5
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
autocurrying
// add.js
import { curry } from 'ramda'
const add = (a, b) => b + a
export default curry(add)
// some other file
import add from './add'
add(2, 3) // 5
add(2)(3) // 5
add()(2)(3) // 5
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
autocurrying
// add.js
import { curry } from 'ramda'
const add = (a, b) => b + a
export default curry(add)
// some other file
import add from './add'
add(2, 3) // 5
add(2)(3) // 5
add()(2)(3) // 5
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
autocurrying
// add.js
import { curry } from 'ramda'
const add = (a, b) => b + a
export default curry(add)
// some other file
import add from './add'
add(2, 3) // 5
add(2)(3) // 5
add()(2)(3) // 5
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
autocurrying
// add.js
import { curry } from 'ramda'
const add = (a, b) => b + a
export default curry(add)
// some other file
import add from './add'
add(2, 3) // 5
add(2)(3) // 5
add()(2)(3) // 5
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
https://rwp.im/ramda-chops-function-currying.html
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 27
sweet! who cares about currying?
currying helps with composition!
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 28
composition
import add from './add'
import mult from './mult'
const add2 = add(2) // partial application
const mult3 = mult(3) // partial application
mult3(add2(10)) // composition & full application
// returns 36
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 29
composition
import add from './add'
import mult from './mult'
const add2 = add(2) // partial application
const mult3 = mult(3) // partial application
mult3(add2(10)) // composition & full application
// returns 36
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 29
composition
import add from './add'
import mult from './mult'
const add2 = add(2) // partial application
const mult3 = mult(3) // partial application
mult3(add2(10)) // composition & full application
// returns 36
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 29
how could we reuse this
composition?
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 30
mult3(add2(10))
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 31
// mult3(add2(10))
const compose = (f, g) => x =>
f(g(x))
const add2mult3 =
compose(mult3, add2)
add2mult3(10) // 36
add2mult3(42) // 132
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
// mult3(add2(10))
const compose = (f, g) => x =>
f(g(x))
const add2mult3 =
compose(mult3, add2)
add2mult3(10) // 36
add2mult3(42) // 132
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
// mult3(add2(10))
const compose = (f, g) => x =>
f(g(x))
const add2mult3 =
compose(mult3, add2)
add2mult3(10) // 36
add2mult3(42) // 132
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
// mult3(add2(10))
const compose = (f, g) => x =>
f(g(x))
const add2mult3 =
compose(mult3, add2)
add2mult3(10) // 36
add2mult3(42) // 132
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
// mult3(add2(10))
const compose = (f, g) => x =>
f(g(x))
const add2mult3 =
compose(mult3, add2)
add2mult3(10) // 36
add2mult3(42) // 132
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
// mult3(add2(10))
const compose = (f, g) => x =>
f(g(x))
const add2mult3 =
compose(mult3, add2)
add2mult3(10) // 36
add2mult3(42) // 132
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
our compose isn't variadic, though
let's reach for a tool
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 33
composition
import { compose } from 'ramda'
import add from './add'
import mult from './mult'
const add2 = add(2)
const mult3 = mult(3)
const add2mult3Repeated =
compose(mult3, add2, mult3, add2)
add2mult3Repeated(10) // 114
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 34
composition
import { compose } from 'ramda'
import add from './add'
import mult from './mult'
const add2 = add(2)
const mult3 = mult(3)
const add2mult3Repeated =
compose(mult3, add2, mult3, add2)
add2mult3Repeated(10) // 114
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 34
composition
import { compose } from 'ramda'
import add from './add'
import mult from './mult'
const add2 = add(2)
const mult3 = mult(3)
const add2mult3Repeated =
compose(mult3, add2, mult3, add2)
add2mult3Repeated(10) // 114
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 34
reading composition
compose(f, g)(x) ≡ f(g(x))
Say it with me, "f after g"
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 35
https://rwp.im/ramda-chops-function-composition.html
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 36
eta-conversion
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 37
eta-conversion
• "An eta conversion (also written η-conversion) is adding or
dropping of abstraction over a function."
https://wiki.haskell.org/Eta_conversion
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 37
eta-conversion
• "An eta conversion (also written η-conversion) is adding or
dropping of abstraction over a function."
https://wiki.haskell.org/Eta_conversion
• eta-abstraction
!
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 37
eta-conversion
• "An eta conversion (also written η-conversion) is adding or
dropping of abstraction over a function."
https://wiki.haskell.org/Eta_conversion
• eta-abstraction
!
• eta-reduction
"
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 37
eta-abstraction
Remember this?
const add2mult3 =
compose(mult3, add2)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 38
eta-abstraction
Remember this?
const add2mult3 =
compose(mult3, add2)
Here's how we would perform an eta-abstraction:
const add2mult3 = n =>
compose(mult3, add2)(n)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 39
eta-reduction
Let's take our recent eta-abstraction,
const add2mult3 = n =>
compose(mult3, add2)(n)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 40
eta-reduction
Let's take our recent eta-abstraction,
const add2mult3 = n =>
compose(mult3, add2)(n)
and perform an eta-reduction:
const add2mult3 =
compose(mult3, add2)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 41
!
we covered the prerequisites!
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 42
let's talk about ramda.js
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
let's talk about ramda.js
• functional library
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
let's talk about ramda.js
• functional library
• easy to make functional pipelines
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
let's talk about ramda.js
• functional library
• easy to make functional pipelines
• never mutates data
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
let's talk about ramda.js
• functional library
• easy to make functional pipelines
• never mutates data
• all functions are automatically curried
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
let's talk about ramda.js
• functional library
• easy to make functional pipelines
• never mutates data
• all functions are automatically curried
• parameters to functions make it convenient for currying; data to
be operated on generally comes last, for example
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
useful ramda functions for us right now
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
useful ramda functions for us right now
• curry :: (* → a) → (* → a)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
useful ramda functions for us right now
• curry :: (* → a) → (* → a)
• compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b,
…, n) → z)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
useful ramda functions for us right now
• curry :: (* → a) → (* → a)
• compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b,
…, n) → z)
• filter :: Filterable f => (a → Boolean) → f a → f a
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
useful ramda functions for us right now
• curry :: (* → a) → (* → a)
• compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b,
…, n) → z)
• filter :: Filterable f => (a → Boolean) → f a → f a
• map :: Functor f => (a → b) → f a → f b
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
useful ramda functions for us right now
• curry :: (* → a) → (* → a)
• compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b,
…, n) → z)
• filter :: Filterable f => (a → Boolean) → f a → f a
• map :: Functor f => (a → b) → f a → f b
• propSatisfies :: (a → Boolean) → String → {String: a} → Boolean
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
useful ramda functions for us right now
• curry :: (* → a) → (* → a)
• compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b,
…, n) → z)
• filter :: Filterable f => (a → Boolean) → f a → f a
• map :: Functor f => (a → b) → f a → f b
• propSatisfies :: (a → Boolean) → String → {String: a} → Boolean
• gte :: Ord a => a → a → Boolean
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
useful ramda functions for us right now
• curry :: (* → a) → (* → a)
• compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b,
…, n) → z)
• filter :: Filterable f => (a → Boolean) → f a → f a
• map :: Functor f => (a → b) → f a → f b
• propSatisfies :: (a → Boolean) → String → {String: a} → Boolean
• gte :: Ord a => a → a → Boolean
• __ (used to specify gaps within curried functions so that we can do partial
application regardless of argument order)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
our original code to refactor
// bestFilmsHtml :: [Film] -> [Html]
const bestFilmsHtml = films => {
const bestFilms = films.filter(film => film.rating >= 8.8)
const filmsHtml = bestFilms.map(film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
)
return filmsHtml
}
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 45
we got this
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 46
implicit return b/c variables weren't needed
const bestFilmsHtml = films =>
films
.filter(film => film.rating >= 8.8)
.map(film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 47
extract hasHighRating
const hasHighRating = film =>
film.rating >= 8.8
const bestFilmsHtml = films =>
films
.filter(hasHighRating)
.map(film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 48
refactor hasHighRating
import { __, gte, propSatisfies } from 'ramda'
const hasHighRating = film =>
propSatisfies(gte(__, 8.8), 'rating')(film)
const bestFilmsHtml = films =>
films
.filter(hasHighRating)
.map(film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 49
eta-reduce hasHighRating
import { __, gte, propSatisfies } from 'ramda'
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
const bestFilmsHtml = films =>
films
.filter(hasHighRating)
.map(film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 50
extract filmHtml
import { __, gte, propSatisfies } from 'ramda'
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
const bestFilmsHtml = films =>
films
.filter(hasHighRating)
.map(filmHtml)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 51
convert bestFilmsHtml to use compose
import { __, compose, filter, gte, map, propSatisfies } from 'ramda'
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
const bestFilmsHtml = films =>
compose(map(filmHtml), filter(hasHighRating))(films)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 52
eta-reduce bestFilmsHtml
import { __, compose, filter, gte, map, propSatisfies } from 'ramda'
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
const bestFilmsHtml =
compose(map(filmHtml), filter(hasHighRating))
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 53
we could stop here...
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 54
import { __, compose, filter, gte, map, propSatisfies } from 'ramda'
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
const bestFilmsHtml =
compose(map(filmHtml), filter(hasHighRating))
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 55
...but let's go for clarity & reuse
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 56
extract onlyHighRatings
import { __, compose, filter, gte, map, propSatisfies } from 'ramda'
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
const onlyHighRatings =
filter(hasHighRating)
const bestFilmsHtml =
compose(map(filmHtml), onlyHighRatings)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 57
extract filmsHtml
import { __, compose, filter, gte, map, propSatisfies } from 'ramda'
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
const filmsHtml =
map(filmHtml)
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
const onlyHighRatings =
filter(hasHighRating)
const bestFilmsHtml =
compose(filmsHtml, onlyHighRatings)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 58
did we miss one? does it matter?
also, why didn't we touch filmHtml?
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 59
import { __, compose, filter, gte, map, propSatisfies } from 'ramda'
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
const filmsHtml =
map(filmHtml)
const hasHighRating =
propSatisfies(gte(__, 8.8), 'rating')
const onlyHighRatings =
filter(hasHighRating)
const bestFilmsHtml =
compose(filmsHtml, onlyHighRatings)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 60
extract isHighRating
import { __, compose, filter, gte, map, propSatisfies } from 'ramda'
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
const filmsHtml =
map(filmHtml)
const isHighRating =
gte(__, 8.8)
const hasHighRating =
propSatisfies(isHighRating, 'rating')
const onlyHighRatings =
filter(hasHighRating)
const bestFilmsHtml =
compose(filmsHtml, onlyHighRatings)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 61
final product
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 62
import { __, compose, filter, gte, map, propSatisfies } from 'ramda'
const filmHtml = film =>
`<div>${film.title}, <strong>${film.rating}</strong></div>`
const filmsHtml =
map(filmHtml)
const isHighRating =
gte(__, 8.8)
const hasHighRating =
propSatisfies(isHighRating, 'rating')
const onlyHighRatings =
filter(hasHighRating)
const bestFilmsHtml =
compose(filmsHtml, onlyHighRatings)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 63
quick examples from other
languages (λ)
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 64
haskell (λ)
loadLines :: FilePath -> IO [String]
loadLines =
fmap lines . readFile
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 65
λ calculus
https://www.lambda-bound.com/book/lambdacalc/node21.html
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 66
a path to point-free JS
Ask yourself,
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 67
a path to point-free JS
Ask yourself,
1. "Do I need these variables in my function?"
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 67
a path to point-free JS
Ask yourself,
1. "Do I need these variables in my function?"
2. "Are my data transformations associative?"
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 67
a path to point-free JS
Ask yourself,
1. "Do I need these variables in my function?"
2. "Are my data transformations associative?"
3. "Am I chaining .then().then().then()s?" or sequencing
effectful expressions? (see composeWith(then))
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 67
a path to point-free JS
Ask yourself,
1. "Do I need these variables in my function?"
2. "Are my data transformations associative?"
3. "Am I chaining .then().then().then()s?" or sequencing
effectful expressions? (see composeWith(then))
4. "Is my function doing more than 1 thing? Should it? Can I reuse
the other things it's doing?"
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 67
a path to point-free JS
At work,
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
a path to point-free JS
At work,
1. write your own compose function and start using it
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
a path to point-free JS
At work,
1. write your own compose function and start using it
2. pull in ramda.js or crocks.js and use a few functions
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
a path to point-free JS
At work,
1. write your own compose function and start using it
2. pull in ramda.js or crocks.js and use a few functions
3. use function naming and types (or pseudo-types) to tell a story
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
a path to point-free JS
At work,
1. write your own compose function and start using it
2. pull in ramda.js or crocks.js and use a few functions
3. use function naming and types (or pseudo-types) to tell a story
4. prefer composition to inheritance
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
a path to point-free JS
At work,
1. write your own compose function and start using it
2. pull in ramda.js or crocks.js and use a few functions
3. use function naming and types (or pseudo-types) to tell a story
4. prefer composition to inheritance
5. try to break down complexity into dumbed-down simplicity ("Don't Make Me Think")
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
a path to point-free JS
At work,
1. write your own compose function and start using it
2. pull in ramda.js or crocks.js and use a few functions
3. use function naming and types (or pseudo-types) to tell a story
4. prefer composition to inheritance
5. try to break down complexity into dumbed-down simplicity ("Don't Make Me Think")
6. genericize as many things as you can so that the only single-use functions are
those directly related to business logic
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
where can i learn more?
• ramda.js
• Functional Programming in JavaScript with Ramda.js
• crocks.js
• Professor Frisby's Mostly Adequate Guide to Functional
Programming
• Point-Free or Die: Tacit Programming in Haskell and Beyond
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 69
where can i learn more?
rwp.im
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 70
A Path to Point-Free JS
web :: rwp.im
github :: rpearce
email :: me@robertwpearce.com
tweeter :: @RobertWPearce
@RobertWPearce | rwp.im | 2019-10-04 charleston.js 71

More Related Content

PDF
Some Functional Programming in JavaScript and Ramda.js
PDF
From Promises & async/await to Async Algebraic Data Types
PDF
FP in JS-Land
PDF
Tracking huge files with Git LFS - LinuxCon 2016
PDF
Tracking large game assets with Git LFS
PDF
Git and Unity
ODP
Eat my data
PDF
Writing dumb tests
Some Functional Programming in JavaScript and Ramda.js
From Promises & async/await to Async Algebraic Data Types
FP in JS-Land
Tracking huge files with Git LFS - LinuxCon 2016
Tracking large game assets with Git LFS
Git and Unity
Eat my data
Writing dumb tests

What's hot (6)

PDF
Git inter-snapshot public
PDF
GIT: Content-addressable filesystem and Version Control System
PPT
Rotzy - Building an iPhone Photo Sharing App on Google App Engine
PDF
DO YOU WANT TO USE A VCS
PDF
성장을 좋아하는 사람이, 성장하고 싶은 사람에게
PDF
Version Control and Git - GitHub Workshop
Git inter-snapshot public
GIT: Content-addressable filesystem and Version Control System
Rotzy - Building an iPhone Photo Sharing App on Google App Engine
DO YOU WANT TO USE A VCS
성장을 좋아하는 사람이, 성장하고 싶은 사람에게
Version Control and Git - GitHub Workshop
Ad

Similar to A Path to Point-Free JavaScript (20)

PPTX
EdTechJoker Spring 2020 - Lecture 8 Drupal again
PDF
Free The Enterprise With Ruby & Master Your Own Domain
PDF
[drupalday2017] - REST in pieces
PDF
REST in pieces
PPT
Lecture 3 - Comm Lab: Web @ ITP
PPTX
F# references (and some misc slides)
PDF
HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0
PDF
The web is too slow
KEY
PDF
You got database in my cloud!
PDF
Mojolicious lite
PPTX
Rise of the Machines: PHP and IoT - php[world] 2016
ZIP
The Power of Open Data
KEY
Rails Presentation (Anton Dmitriyev)
PDF
Modern Release Engineering in a Nutshell - Why Researchers should Care!
PDF
10 things about BDD, Cucumber and SpecFlow - Long Version 2016
PPTX
20100614 ISWSA Keynote
PDF
20160229 open belgium the city of ghent as linked open data
PDF
Open City, Smart City - Ann Bernaert
PDF
Origins of Serverless
EdTechJoker Spring 2020 - Lecture 8 Drupal again
Free The Enterprise With Ruby & Master Your Own Domain
[drupalday2017] - REST in pieces
REST in pieces
Lecture 3 - Comm Lab: Web @ ITP
F# references (and some misc slides)
HTMX: Web 1.0 with the benefits of Web 2.0 without the grift of Web 3.0
The web is too slow
You got database in my cloud!
Mojolicious lite
Rise of the Machines: PHP and IoT - php[world] 2016
The Power of Open Data
Rails Presentation (Anton Dmitriyev)
Modern Release Engineering in a Nutshell - Why Researchers should Care!
10 things about BDD, Cucumber and SpecFlow - Long Version 2016
20100614 ISWSA Keynote
20160229 open belgium the city of ghent as linked open data
Open City, Smart City - Ann Bernaert
Origins of Serverless
Ad

More from Robert Pearce (7)

PDF
How to Lose Functional Programming at Work
PDF
hakyll – haskell static site generator
PDF
Behaviour & Your Team
PDF
Static sites with react
PDF
React.js Basics - ConvergeSE 2015
PDF
JavaScript 101 - Class 2
PDF
JavaScript 101 - Class 1
How to Lose Functional Programming at Work
hakyll – haskell static site generator
Behaviour & Your Team
Static sites with react
React.js Basics - ConvergeSE 2015
JavaScript 101 - Class 2
JavaScript 101 - Class 1

Recently uploaded (20)

PPTX
Monitoring Stack: Grafana, Loki & Promtail
PDF
Cost to Outsource Software Development in 2025
PPTX
WiFi Honeypot Detecscfddssdffsedfseztor.pptx
PDF
iTop VPN Crack Latest Version Full Key 2025
PPTX
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
PDF
CCleaner Pro 6.38.11537 Crack Final Latest Version 2025
PDF
Tally Prime Crack Download New Version 5.1 [2025] (License Key Free
DOCX
Greta — No-Code AI for Building Full-Stack Web & Mobile Apps
PDF
How to Make Money in the Metaverse_ Top Strategies for Beginners.pdf
PDF
Wondershare Filmora 15 Crack With Activation Key [2025
PPTX
Weekly report ppt - harsh dattuprasad patel.pptx
PDF
17 Powerful Integrations Your Next-Gen MLM Software Needs
PPTX
Log360_SIEM_Solutions Overview PPT_Feb 2020.pptx
PPTX
Oracle Fusion HCM Cloud Demo for Beginners
PDF
Complete Guide to Website Development in Malaysia for SMEs
PDF
Navsoft: AI-Powered Business Solutions & Custom Software Development
PDF
Internet Downloader Manager (IDM) Crack 6.42 Build 41
PDF
Download FL Studio Crack Latest version 2025 ?
PDF
Autodesk AutoCAD Crack Free Download 2025
PPTX
Patient Appointment Booking in Odoo with online payment
Monitoring Stack: Grafana, Loki & Promtail
Cost to Outsource Software Development in 2025
WiFi Honeypot Detecscfddssdffsedfseztor.pptx
iTop VPN Crack Latest Version Full Key 2025
Embracing Complexity in Serverless! GOTO Serverless Bengaluru
CCleaner Pro 6.38.11537 Crack Final Latest Version 2025
Tally Prime Crack Download New Version 5.1 [2025] (License Key Free
Greta — No-Code AI for Building Full-Stack Web & Mobile Apps
How to Make Money in the Metaverse_ Top Strategies for Beginners.pdf
Wondershare Filmora 15 Crack With Activation Key [2025
Weekly report ppt - harsh dattuprasad patel.pptx
17 Powerful Integrations Your Next-Gen MLM Software Needs
Log360_SIEM_Solutions Overview PPT_Feb 2020.pptx
Oracle Fusion HCM Cloud Demo for Beginners
Complete Guide to Website Development in Malaysia for SMEs
Navsoft: AI-Powered Business Solutions & Custom Software Development
Internet Downloader Manager (IDM) Crack 6.42 Build 41
Download FL Studio Crack Latest version 2025 ?
Autodesk AutoCAD Crack Free Download 2025
Patient Appointment Booking in Odoo with online payment

A Path to Point-Free JavaScript

  • 1. A Path to Point-Free JS web :: rwp.im github :: rpearce email :: me@robertwpearce.com tweeter :: @RobertWPearce @RobertWPearce | rwp.im | 2019-10-04 charleston.js 1
  • 2. about me Hi, I'm Robert @RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
  • 3. about me Hi, I'm Robert • My kiwi wife & I have recently moved back from New Zealand @RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
  • 4. about me Hi, I'm Robert • My kiwi wife & I have recently moved back from New Zealand • Started out at Jack Russell Software w/ Tom Wilson in 2011 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
  • 5. about me Hi, I'm Robert • My kiwi wife & I have recently moved back from New Zealand • Started out at Jack Russell Software w/ Tom Wilson in 2011 • Currently with Articulate, the e-learning software company @RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
  • 6. about me Hi, I'm Robert • My kiwi wife & I have recently moved back from New Zealand • Started out at Jack Russell Software w/ Tom Wilson in 2011 • Currently with Articulate, the e-learning software company • Work with FP in JS all day @RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
  • 7. about me Hi, I'm Robert • My kiwi wife & I have recently moved back from New Zealand • Started out at Jack Russell Software w/ Tom Wilson in 2011 • Currently with Articulate, the e-learning software company • Work with FP in JS all day • Learn Haskell (λ) and other FP in what little free time I have @RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
  • 8. about me Hi, I'm Robert • My kiwi wife & I have recently moved back from New Zealand • Started out at Jack Russell Software w/ Tom Wilson in 2011 • Currently with Articulate, the e-learning software company • Work with FP in JS all day • Learn Haskell (λ) and other FP in what little free time I have • Pretend to know what I'm doing @RobertWPearce | rwp.im | 2019-10-04 charleston.js 2
  • 9. Question: what are we going to do? @RobertWPearce | rwp.im | 2019-10-04 charleston.js 3
  • 10. Answer: ultimately attempt to simplify this code // bestFilmsHtml :: [Film] -> [Html] const bestFilmsHtml = films => { const bestFilms = films.filter(film => film.rating >= 8.8) const filmsHtml = bestFilms.map(film => `<div>${film.title}, <strong>${film.rating}</strong></div>` ) return filmsHtml } @RobertWPearce | rwp.im | 2019-10-04 charleston.js 4
  • 11. Answer: which takes this data... 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 } ] @RobertWPearce | rwp.im | 2019-10-04 charleston.js 5
  • 12. Answer: ...and returns this [ "<div>The Empire Strikes Back, <strong>8.8</strong></div>", "<div>Pulp Fiction, <strong>8.9</strong></div>" ] @RobertWPearce | rwp.im | 2019-10-04 charleston.js 6
  • 13. Question: how are we going to do that? @RobertWPearce | rwp.im | 2019-10-04 charleston.js 7
  • 14. Answer: @RobertWPearce | rwp.im | 2019-10-04 charleston.js 8
  • 15. Answer: • ! currying! @RobertWPearce | rwp.im | 2019-10-04 charleston.js 8
  • 19. Answer: • ! currying! • " composition! • # $ eta-conversion! • % ramda.js! • ...this is our Friday night ✨ @RobertWPearce | rwp.im | 2019-10-04 charleston.js 8
  • 20. Question: what does point-free mean? @RobertWPearce | rwp.im | 2019-10-04 charleston.js 9
  • 21. Tacit programming, also called point-free style, is a programming paradigm in which function definitions do not identify the arguments (or "points") on which they operate. Instead the definitions merely compose other functions, among which are combinators that manipulate the arguments. — https://en.wikipedia.org/wiki/Tacit_programming @RobertWPearce | rwp.im | 2019-10-04 charleston.js 10
  • 22. Combinatory logic is a notation to eliminate the need for quantified variables in mathematical logic [...] based on combinators which were introduced by Schönfinkel in 1920 with the idea of providing an analogous way to build up functions - and to remove any mention of variables - particularly in predicate logic. A combinator is a higher-order function that uses only function application and earlier defined combinators to define a result from its arguments. — https://en.wikipedia.org/wiki/Combinatory_logic @RobertWPearce | rwp.im | 2019-10-04 charleston.js 11
  • 23. ...there are other opinions @RobertWPearce | rwp.im | 2019-10-04 charleston.js 12
  • 24. The lack of argument naming gives point-free style a reputation of being unnecessarily obscure, hence the epithet "pointless style". — https://en.wikipedia.org/wiki/Tacit_programming @RobertWPearce | rwp.im | 2019-10-04 charleston.js 13
  • 25. some may disagree "Point-Free or Die: Tacit Programming in Haskell and Beyond" https://www.youtube.com/watch?v=seVSlKazsNk @RobertWPearce | rwp.im | 2019-10-04 charleston.js 14
  • 26. Question: what does it look like? @RobertWPearce | rwp.im | 2019-10-04 charleston.js 15
  • 27. (do we care about the implementation?) (say yes) 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 } ] bestFilmsHtml(films) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>" // ] @RobertWPearce | rwp.im | 2019-10-04 charleston.js 16
  • 28. (do we care about the implementation?) (say yes) 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 } ] bestFilmsHtml(films) // => [ // "<div>The Empire Strikes Back, <strong>8.8</strong></div>", // "<div>Pulp Fiction, <strong>8.9</strong></div>" // ] @RobertWPearce | rwp.im | 2019-10-04 charleston.js 16
  • 29. bestFilmsHtml // bestFilmsHtml :: [Film] -> [Html] const bestFilmsHtml = compose(filmsHtml, onlyHighRatings) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 17
  • 30. onlyHighRatings // onlyHighRatings :: [Film] -> [Film] const onlyHighRatings = filter(hasHighRating) // bestFilmsHtml :: [Film] -> [Html] const bestFilmsHtml = compose(filmsHtml, onlyHighRatings) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 18
  • 31. hasHighRating // hasHighRating :: Film -> Bool const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') // onlyHighRatings :: [Film] -> [Film] const onlyHighRatings = filter(hasHighRating) // bestFilmsHtml :: [Film] -> [Html] const bestFilmsHtml = compose(filmsHtml, onlyHighRatings) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 19
  • 32. filmsHtml // filmsHtml :: [Film] -> [Html] const filmsHtml = map(filmHtml) // hasHighRating :: Film -> Bool const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') // onlyHighRatings :: [Film] -> [Film] const onlyHighRatings = filter(hasHighRating) // bestFilmsHtml :: [Film] -> [Html] const bestFilmsHtml = compose(filmsHtml, onlyHighRatings) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 20
  • 33. filmHtml (notice any difference?) // filmHtml :: Film -> Html const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` // filmsHtml :: [Film] -> [Html] const filmsHtml = map(filmHtml) // hasHighRating :: Film -> Bool const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') // onlyHighRatings :: [Film] -> [Film] const onlyHighRatings = filter(hasHighRating) // bestFilmsHtml :: [Film] -> [Html] const bestFilmsHtml = compose(filmsHtml, onlyHighRatings) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 21
  • 34. filmHtml (notice any difference?) // filmHtml :: Film -> Html const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` // filmsHtml :: [Film] -> [Html] const filmsHtml = map(filmHtml) // hasHighRating :: Film -> Bool const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') // onlyHighRatings :: [Film] -> [Film] const onlyHighRatings = filter(hasHighRating) // bestFilmsHtml :: [Film] -> [Html] const bestFilmsHtml = compose(filmsHtml, onlyHighRatings) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 21
  • 35. ! buckle up it's time for prerequisites @RobertWPearce | rwp.im | 2019-10-04 charleston.js 22
  • 36. currying ! and partial application const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 add2(5) // 7 const add = a => b => b + a const add3 = add(3) add3(2) // 5 add3(4) // 7 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
  • 37. currying ! and partial application const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 add2(5) // 7 const add = a => b => b + a const add3 = add(3) add3(2) // 5 add3(4) // 7 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
  • 38. currying ! and partial application const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 add2(5) // 7 const add = a => b => b + a const add3 = add(3) add3(2) // 5 add3(4) // 7 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
  • 39. currying ! and partial application const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 add2(5) // 7 const add = a => b => b + a const add3 = add(3) add3(2) // 5 add3(4) // 7 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
  • 40. currying ! and partial application const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 add2(5) // 7 const add = a => b => b + a const add3 = add(3) add3(2) // 5 add3(4) // 7 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
  • 41. currying ! and partial application const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 add2(5) // 7 const add = a => b => b + a const add3 = add(3) add3(2) // 5 add3(4) // 7 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
  • 42. currying ! and partial application const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 add2(5) // 7 const add = a => b => b + a const add3 = add(3) add3(2) // 5 add3(4) // 7 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
  • 43. currying ! and partial application const add = (a, b) => b + a const add2 = add.bind(null, 2) add2(3) // 5 add2(5) // 7 const add = a => b => b + a const add3 = add(3) add3(2) // 5 add3(4) // 7 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 23
  • 44. In mathematics and computer science, currying is the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument. — https://en.wikipedia.org/wiki/Currying @RobertWPearce | rwp.im | 2019-10-04 charleston.js 24
  • 45. currying ! and partial application const add = a => b => b + a // is equivalent to function add(a) { return function(b) { return b + a } } // but... add(2)(3) // 5 add(2, 3) // function add() ... ( °□° @RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
  • 46. currying ! and partial application const add = a => b => b + a // is equivalent to function add(a) { return function(b) { return b + a } } // but... add(2)(3) // 5 add(2, 3) // function add() ... ( °□° @RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
  • 47. currying ! and partial application const add = a => b => b + a // is equivalent to function add(a) { return function(b) { return b + a } } // but... add(2)(3) // 5 add(2, 3) // function add() ... ( °□° @RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
  • 48. currying ! and partial application const add = a => b => b + a // is equivalent to function add(a) { return function(b) { return b + a } } // but... add(2)(3) // 5 add(2, 3) // function add() ... ( °□° @RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
  • 49. currying ! and partial application const add = a => b => b + a // is equivalent to function add(a) { return function(b) { return b + a } } // but... add(2)(3) // 5 add(2, 3) // function add() ... ( °□° @RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
  • 50. currying ! and partial application const add = a => b => b + a // is equivalent to function add(a) { return function(b) { return b + a } } // but... add(2)(3) // 5 add(2, 3) // function add() ... ( °□° @RobertWPearce | rwp.im | 2019-10-04 charleston.js 25
  • 51. autocurrying // add.js import { curry } from 'ramda' const add = (a, b) => b + a export default curry(add) // some other file import add from './add' add(2, 3) // 5 add(2)(3) // 5 add()(2)(3) // 5 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
  • 52. autocurrying // add.js import { curry } from 'ramda' const add = (a, b) => b + a export default curry(add) // some other file import add from './add' add(2, 3) // 5 add(2)(3) // 5 add()(2)(3) // 5 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
  • 53. autocurrying // add.js import { curry } from 'ramda' const add = (a, b) => b + a export default curry(add) // some other file import add from './add' add(2, 3) // 5 add(2)(3) // 5 add()(2)(3) // 5 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
  • 54. autocurrying // add.js import { curry } from 'ramda' const add = (a, b) => b + a export default curry(add) // some other file import add from './add' add(2, 3) // 5 add(2)(3) // 5 add()(2)(3) // 5 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
  • 55. autocurrying // add.js import { curry } from 'ramda' const add = (a, b) => b + a export default curry(add) // some other file import add from './add' add(2, 3) // 5 add(2)(3) // 5 add()(2)(3) // 5 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
  • 56. autocurrying // add.js import { curry } from 'ramda' const add = (a, b) => b + a export default curry(add) // some other file import add from './add' add(2, 3) // 5 add(2)(3) // 5 add()(2)(3) // 5 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
  • 57. autocurrying // add.js import { curry } from 'ramda' const add = (a, b) => b + a export default curry(add) // some other file import add from './add' add(2, 3) // 5 add(2)(3) // 5 add()(2)(3) // 5 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
  • 58. autocurrying // add.js import { curry } from 'ramda' const add = (a, b) => b + a export default curry(add) // some other file import add from './add' add(2, 3) // 5 add(2)(3) // 5 add()(2)(3) // 5 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
  • 59. autocurrying // add.js import { curry } from 'ramda' const add = (a, b) => b + a export default curry(add) // some other file import add from './add' add(2, 3) // 5 add(2)(3) // 5 add()(2)(3) // 5 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
  • 60. autocurrying // add.js import { curry } from 'ramda' const add = (a, b) => b + a export default curry(add) // some other file import add from './add' add(2, 3) // 5 add(2)(3) // 5 add()(2)(3) // 5 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 26
  • 62. sweet! who cares about currying? currying helps with composition! @RobertWPearce | rwp.im | 2019-10-04 charleston.js 28
  • 63. composition import add from './add' import mult from './mult' const add2 = add(2) // partial application const mult3 = mult(3) // partial application mult3(add2(10)) // composition & full application // returns 36 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 29
  • 64. composition import add from './add' import mult from './mult' const add2 = add(2) // partial application const mult3 = mult(3) // partial application mult3(add2(10)) // composition & full application // returns 36 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 29
  • 65. composition import add from './add' import mult from './mult' const add2 = add(2) // partial application const mult3 = mult(3) // partial application mult3(add2(10)) // composition & full application // returns 36 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 29
  • 66. how could we reuse this composition? @RobertWPearce | rwp.im | 2019-10-04 charleston.js 30
  • 67. mult3(add2(10)) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 31
  • 68. // mult3(add2(10)) const compose = (f, g) => x => f(g(x)) const add2mult3 = compose(mult3, add2) add2mult3(10) // 36 add2mult3(42) // 132 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
  • 69. // mult3(add2(10)) const compose = (f, g) => x => f(g(x)) const add2mult3 = compose(mult3, add2) add2mult3(10) // 36 add2mult3(42) // 132 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
  • 70. // mult3(add2(10)) const compose = (f, g) => x => f(g(x)) const add2mult3 = compose(mult3, add2) add2mult3(10) // 36 add2mult3(42) // 132 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
  • 71. // mult3(add2(10)) const compose = (f, g) => x => f(g(x)) const add2mult3 = compose(mult3, add2) add2mult3(10) // 36 add2mult3(42) // 132 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
  • 72. // mult3(add2(10)) const compose = (f, g) => x => f(g(x)) const add2mult3 = compose(mult3, add2) add2mult3(10) // 36 add2mult3(42) // 132 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
  • 73. // mult3(add2(10)) const compose = (f, g) => x => f(g(x)) const add2mult3 = compose(mult3, add2) add2mult3(10) // 36 add2mult3(42) // 132 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 32
  • 74. our compose isn't variadic, though let's reach for a tool @RobertWPearce | rwp.im | 2019-10-04 charleston.js 33
  • 75. composition import { compose } from 'ramda' import add from './add' import mult from './mult' const add2 = add(2) const mult3 = mult(3) const add2mult3Repeated = compose(mult3, add2, mult3, add2) add2mult3Repeated(10) // 114 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 34
  • 76. composition import { compose } from 'ramda' import add from './add' import mult from './mult' const add2 = add(2) const mult3 = mult(3) const add2mult3Repeated = compose(mult3, add2, mult3, add2) add2mult3Repeated(10) // 114 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 34
  • 77. composition import { compose } from 'ramda' import add from './add' import mult from './mult' const add2 = add(2) const mult3 = mult(3) const add2mult3Repeated = compose(mult3, add2, mult3, add2) add2mult3Repeated(10) // 114 @RobertWPearce | rwp.im | 2019-10-04 charleston.js 34
  • 78. reading composition compose(f, g)(x) ≡ f(g(x)) Say it with me, "f after g" @RobertWPearce | rwp.im | 2019-10-04 charleston.js 35
  • 80. eta-conversion @RobertWPearce | rwp.im | 2019-10-04 charleston.js 37
  • 81. eta-conversion • "An eta conversion (also written η-conversion) is adding or dropping of abstraction over a function." https://wiki.haskell.org/Eta_conversion @RobertWPearce | rwp.im | 2019-10-04 charleston.js 37
  • 82. eta-conversion • "An eta conversion (also written η-conversion) is adding or dropping of abstraction over a function." https://wiki.haskell.org/Eta_conversion • eta-abstraction ! @RobertWPearce | rwp.im | 2019-10-04 charleston.js 37
  • 83. eta-conversion • "An eta conversion (also written η-conversion) is adding or dropping of abstraction over a function." https://wiki.haskell.org/Eta_conversion • eta-abstraction ! • eta-reduction " @RobertWPearce | rwp.im | 2019-10-04 charleston.js 37
  • 84. eta-abstraction Remember this? const add2mult3 = compose(mult3, add2) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 38
  • 85. eta-abstraction Remember this? const add2mult3 = compose(mult3, add2) Here's how we would perform an eta-abstraction: const add2mult3 = n => compose(mult3, add2)(n) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 39
  • 86. eta-reduction Let's take our recent eta-abstraction, const add2mult3 = n => compose(mult3, add2)(n) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 40
  • 87. eta-reduction Let's take our recent eta-abstraction, const add2mult3 = n => compose(mult3, add2)(n) and perform an eta-reduction: const add2mult3 = compose(mult3, add2) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 41
  • 88. ! we covered the prerequisites! @RobertWPearce | rwp.im | 2019-10-04 charleston.js 42
  • 89. let's talk about ramda.js @RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
  • 90. let's talk about ramda.js • functional library @RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
  • 91. let's talk about ramda.js • functional library • easy to make functional pipelines @RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
  • 92. let's talk about ramda.js • functional library • easy to make functional pipelines • never mutates data @RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
  • 93. let's talk about ramda.js • functional library • easy to make functional pipelines • never mutates data • all functions are automatically curried @RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
  • 94. let's talk about ramda.js • functional library • easy to make functional pipelines • never mutates data • all functions are automatically curried • parameters to functions make it convenient for currying; data to be operated on generally comes last, for example @RobertWPearce | rwp.im | 2019-10-04 charleston.js 43
  • 95. useful ramda functions for us right now @RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
  • 96. useful ramda functions for us right now • curry :: (* → a) → (* → a) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
  • 97. useful ramda functions for us right now • curry :: (* → a) → (* → a) • compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b, …, n) → z) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
  • 98. useful ramda functions for us right now • curry :: (* → a) → (* → a) • compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b, …, n) → z) • filter :: Filterable f => (a → Boolean) → f a → f a @RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
  • 99. useful ramda functions for us right now • curry :: (* → a) → (* → a) • compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b, …, n) → z) • filter :: Filterable f => (a → Boolean) → f a → f a • map :: Functor f => (a → b) → f a → f b @RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
  • 100. useful ramda functions for us right now • curry :: (* → a) → (* → a) • compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b, …, n) → z) • filter :: Filterable f => (a → Boolean) → f a → f a • map :: Functor f => (a → b) → f a → f b • propSatisfies :: (a → Boolean) → String → {String: a} → Boolean @RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
  • 101. useful ramda functions for us right now • curry :: (* → a) → (* → a) • compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b, …, n) → z) • filter :: Filterable f => (a → Boolean) → f a → f a • map :: Functor f => (a → b) → f a → f b • propSatisfies :: (a → Boolean) → String → {String: a} → Boolean • gte :: Ord a => a → a → Boolean @RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
  • 102. useful ramda functions for us right now • curry :: (* → a) → (* → a) • compose :: ((y → z), (x → y), …, (o → p), ((a, b, …, n) → o)) → ((a, b, …, n) → z) • filter :: Filterable f => (a → Boolean) → f a → f a • map :: Functor f => (a → b) → f a → f b • propSatisfies :: (a → Boolean) → String → {String: a} → Boolean • gte :: Ord a => a → a → Boolean • __ (used to specify gaps within curried functions so that we can do partial application regardless of argument order) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 44
  • 103. our original code to refactor // bestFilmsHtml :: [Film] -> [Html] const bestFilmsHtml = films => { const bestFilms = films.filter(film => film.rating >= 8.8) const filmsHtml = bestFilms.map(film => `<div>${film.title}, <strong>${film.rating}</strong></div>` ) return filmsHtml } @RobertWPearce | rwp.im | 2019-10-04 charleston.js 45
  • 104. we got this @RobertWPearce | rwp.im | 2019-10-04 charleston.js 46
  • 105. implicit return b/c variables weren't needed const bestFilmsHtml = films => films .filter(film => film.rating >= 8.8) .map(film => `<div>${film.title}, <strong>${film.rating}</strong></div>` ) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 47
  • 106. extract hasHighRating const hasHighRating = film => film.rating >= 8.8 const bestFilmsHtml = films => films .filter(hasHighRating) .map(film => `<div>${film.title}, <strong>${film.rating}</strong></div>` ) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 48
  • 107. refactor hasHighRating import { __, gte, propSatisfies } from 'ramda' const hasHighRating = film => propSatisfies(gte(__, 8.8), 'rating')(film) const bestFilmsHtml = films => films .filter(hasHighRating) .map(film => `<div>${film.title}, <strong>${film.rating}</strong></div>` ) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 49
  • 108. eta-reduce hasHighRating import { __, gte, propSatisfies } from 'ramda' const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') const bestFilmsHtml = films => films .filter(hasHighRating) .map(film => `<div>${film.title}, <strong>${film.rating}</strong></div>` ) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 50
  • 109. extract filmHtml import { __, gte, propSatisfies } from 'ramda' const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') const bestFilmsHtml = films => films .filter(hasHighRating) .map(filmHtml) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 51
  • 110. convert bestFilmsHtml to use compose import { __, compose, filter, gte, map, propSatisfies } from 'ramda' const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') const bestFilmsHtml = films => compose(map(filmHtml), filter(hasHighRating))(films) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 52
  • 111. eta-reduce bestFilmsHtml import { __, compose, filter, gte, map, propSatisfies } from 'ramda' const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') const bestFilmsHtml = compose(map(filmHtml), filter(hasHighRating)) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 53
  • 112. we could stop here... @RobertWPearce | rwp.im | 2019-10-04 charleston.js 54
  • 113. import { __, compose, filter, gte, map, propSatisfies } from 'ramda' const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') const bestFilmsHtml = compose(map(filmHtml), filter(hasHighRating)) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 55
  • 114. ...but let's go for clarity & reuse @RobertWPearce | rwp.im | 2019-10-04 charleston.js 56
  • 115. extract onlyHighRatings import { __, compose, filter, gte, map, propSatisfies } from 'ramda' const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') const onlyHighRatings = filter(hasHighRating) const bestFilmsHtml = compose(map(filmHtml), onlyHighRatings) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 57
  • 116. extract filmsHtml import { __, compose, filter, gte, map, propSatisfies } from 'ramda' const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` const filmsHtml = map(filmHtml) const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') const onlyHighRatings = filter(hasHighRating) const bestFilmsHtml = compose(filmsHtml, onlyHighRatings) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 58
  • 117. did we miss one? does it matter? also, why didn't we touch filmHtml? @RobertWPearce | rwp.im | 2019-10-04 charleston.js 59
  • 118. import { __, compose, filter, gte, map, propSatisfies } from 'ramda' const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` const filmsHtml = map(filmHtml) const hasHighRating = propSatisfies(gte(__, 8.8), 'rating') const onlyHighRatings = filter(hasHighRating) const bestFilmsHtml = compose(filmsHtml, onlyHighRatings) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 60
  • 119. extract isHighRating import { __, compose, filter, gte, map, propSatisfies } from 'ramda' const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` const filmsHtml = map(filmHtml) const isHighRating = gte(__, 8.8) const hasHighRating = propSatisfies(isHighRating, 'rating') const onlyHighRatings = filter(hasHighRating) const bestFilmsHtml = compose(filmsHtml, onlyHighRatings) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 61
  • 120. final product @RobertWPearce | rwp.im | 2019-10-04 charleston.js 62
  • 121. import { __, compose, filter, gte, map, propSatisfies } from 'ramda' const filmHtml = film => `<div>${film.title}, <strong>${film.rating}</strong></div>` const filmsHtml = map(filmHtml) const isHighRating = gte(__, 8.8) const hasHighRating = propSatisfies(isHighRating, 'rating') const onlyHighRatings = filter(hasHighRating) const bestFilmsHtml = compose(filmsHtml, onlyHighRatings) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 63
  • 122. quick examples from other languages (λ) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 64
  • 123. haskell (λ) loadLines :: FilePath -> IO [String] loadLines = fmap lines . readFile @RobertWPearce | rwp.im | 2019-10-04 charleston.js 65
  • 125. a path to point-free JS Ask yourself, @RobertWPearce | rwp.im | 2019-10-04 charleston.js 67
  • 126. a path to point-free JS Ask yourself, 1. "Do I need these variables in my function?" @RobertWPearce | rwp.im | 2019-10-04 charleston.js 67
  • 127. a path to point-free JS Ask yourself, 1. "Do I need these variables in my function?" 2. "Are my data transformations associative?" @RobertWPearce | rwp.im | 2019-10-04 charleston.js 67
  • 128. a path to point-free JS Ask yourself, 1. "Do I need these variables in my function?" 2. "Are my data transformations associative?" 3. "Am I chaining .then().then().then()s?" or sequencing effectful expressions? (see composeWith(then)) @RobertWPearce | rwp.im | 2019-10-04 charleston.js 67
  • 129. a path to point-free JS Ask yourself, 1. "Do I need these variables in my function?" 2. "Are my data transformations associative?" 3. "Am I chaining .then().then().then()s?" or sequencing effectful expressions? (see composeWith(then)) 4. "Is my function doing more than 1 thing? Should it? Can I reuse the other things it's doing?" @RobertWPearce | rwp.im | 2019-10-04 charleston.js 67
  • 130. a path to point-free JS At work, @RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
  • 131. a path to point-free JS At work, 1. write your own compose function and start using it @RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
  • 132. a path to point-free JS At work, 1. write your own compose function and start using it 2. pull in ramda.js or crocks.js and use a few functions @RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
  • 133. a path to point-free JS At work, 1. write your own compose function and start using it 2. pull in ramda.js or crocks.js and use a few functions 3. use function naming and types (or pseudo-types) to tell a story @RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
  • 134. a path to point-free JS At work, 1. write your own compose function and start using it 2. pull in ramda.js or crocks.js and use a few functions 3. use function naming and types (or pseudo-types) to tell a story 4. prefer composition to inheritance @RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
  • 135. a path to point-free JS At work, 1. write your own compose function and start using it 2. pull in ramda.js or crocks.js and use a few functions 3. use function naming and types (or pseudo-types) to tell a story 4. prefer composition to inheritance 5. try to break down complexity into dumbed-down simplicity ("Don't Make Me Think") @RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
  • 136. a path to point-free JS At work, 1. write your own compose function and start using it 2. pull in ramda.js or crocks.js and use a few functions 3. use function naming and types (or pseudo-types) to tell a story 4. prefer composition to inheritance 5. try to break down complexity into dumbed-down simplicity ("Don't Make Me Think") 6. genericize as many things as you can so that the only single-use functions are those directly related to business logic @RobertWPearce | rwp.im | 2019-10-04 charleston.js 68
  • 137. where can i learn more? • ramda.js • Functional Programming in JavaScript with Ramda.js • crocks.js • Professor Frisby's Mostly Adequate Guide to Functional Programming • Point-Free or Die: Tacit Programming in Haskell and Beyond @RobertWPearce | rwp.im | 2019-10-04 charleston.js 69
  • 138. where can i learn more? rwp.im @RobertWPearce | rwp.im | 2019-10-04 charleston.js 70
  • 139. A Path to Point-Free JS web :: rwp.im github :: rpearce email :: me@robertwpearce.com tweeter :: @RobertWPearce @RobertWPearce | rwp.im | 2019-10-04 charleston.js 71