KEMBAR78
Functional JS for everyone - 4Developers | PDF
Functional JavaScript
for everyone
Bartek Witczak
1/320
http://te.xel.io/posts/2015-02-22-category-theory-and-human-behavior.html
https://prateekvjoshi.com/2014/11/01/functors-in-c/
Functions
function youngerThan(limit, age) {
return age < limit
}
function youngerThan(limit) {
return function(age) {
return age < limit
}
}
function youngerThan(limit) {
return function(age) {
return age < limit
}
}
const youngerThanMe = youngerThan(28)
...
people.map(youngerThanMe)
In programming language design, a first-class citizen (…) is an
entity which supports all the operations generally available to
other entities. These operations typically include being passed
as an argument, returned from a function, and assigned to a
variable
let languages = ['JavaScript', 'Scala', 'Go']
...
...
...
...
let divs = []
for(let i = 0; i < languages.length; i++) {
divs.push(`<div>${langs[i]}</div>`)
}
let languages = ['JavaScript', 'Scala', 'Go']
languages.push('Java')
languages.push('Ruby')
languages.splice(0, 3)
let divs = []
for(let i = 0; i < languages.length; i++) {
divs.push(`<div>${languages[i]}</div>`)
}
const languages = Immutable.List.of(
‘JavaScript',
‘Scala',
‘Go'
)
...
...
let divs = []
for(let i = 0; i < languages.length; i++)
{
divs.push(`<div>${languages[i]}</div>`)
}
• always evaluates to same result for the same
input
• evaluation does not cause any semantically
observable side effects
Pure function
let user = {...}
function createMessage(text) {
const message = Db.save(text)
return message
}
function sendMessage(text) {
const message = createMessage(text)
const messageId = MessageService.send(message, user)
return messageId
}
IMPURE
function createMessage(Db, text) {
const message = Db.save(text)
return message
}
function sendMessage(Db, MessageService, text, user) {
const message = createMessage(Db, text)
const messageId = MessageService.send(message, user)
return messageId
}
PURE
function createMessage(Db, text) {
const message = Db.save(text)
return message
}
function sendMessage(Db, MessageService, text, user) {
const message = createMessage(Db, text)
const messageId = MessageService.send(message, user)
return messageId
}
Explicit dependency / Self-documenting
Testable
function createMessage(Db, text) {
const message = Db.save(text)
return message
}
function sendMessage(Db, MessageService, text, user) {
const message = createMessage(Db, text)
const messageId = MessageService.send(message, user)
return messageId
}
Resonable
function createMessage(Db, text) {
const message = Db.save(text)
return message
}
function sendMessage(Db, MessageService, text, user) {
const message = createMessage(Db, text)
const messageId = MessageService.send(message, user)
return messageId
}
The problem with object-oriented languages is they’ve got all
this implicit environment that they carry around with them. You
wanted a banana but what you got was a gorilla holding the
banana... and the entire jungle
Joe Armstrong, Erlang creator
Currying
f ( x, y ) === f ( x ) ( y )
function add(x, y) {
return x + y
}
add(2, 3)
function curried_add (x) {
return function (y) {
return x + y
}
}
curried_add(2)(3)
const increment = curried_add(1)
increment(5)
peopleAge.map(increment)
const add10 = curried_add(10)
add10(15)
export const create = (fetch) => {
return {
...
post: (url, data, token) => {
const secureHeader = token ? { 'SECURE-TOKEN':
token } : {}
return Q(fetch(url, {
method: 'post',
headers: _.assign({
'Accept': 'application/json',
'Content-Type': 'application/json'
}, secureHeader),
body: JSON.stringify(data)
}))
}
...
}
export default create(fetch)
const token = '1jflann24kh11;2114fanakf'
http.post(someURL, { user: 'Henry' }, token)
return {
...
post: (token, url, data) => {
...
post(token)(url)(data)
currying
const createSecured = (http, token) => {
...
post: http.post(token)
...
}
sercureHttp.post(someURL)({ user: 'Henry' })
No more loops
const languages = ['JavaScript', 'Scala', 'Haskell']
const divs = []
for(let i = 0; i < languages.length; i++) {
divs.push(`<div>${languages[i]}</div>`)
}
const languages = ['JavaScript', 'Scala', 'Haskell']
const divs = languages.map(l => `<div>${l}</div>`)
const dimensions = [100, 231, 84]
const sum = 0
for(let i = 0; i < dimensions.length; i++) {
sum += dimensions[i]
}
const dimensions = [100, 231, 84]
const sum = dimensions.reduce((a, b) => a + b, 0)
const people = [{name: 'Henry', age: 21},
... ]
const adults = []
for(let i = 0; i < people.length; i++) {
if(people[i].age >= 18) {
adults.push(people[i])
}
}
const people = [{name: 'Henry', age: 21},
... ]
const adults = people.filter(p => p.age >= 18)
Function composition
f(g(x)) === (f ∘ g)(x)
function compose(f, g) {
return function(x) {
return f(g(x))
}
}
const add2 = (x) => x + 2
const multiplyBy2 = (x) => x * 2
multiplyBy2(add2(3))
const add2ThenMultiplyBy2 = compose(multiplyBy2, add2)
add2ThenMultiplyBy2(3)
function convertToAlbums(json) {
const artist = extractArtist(json)
const album = ...
return album
}
function extractTracks(album) {
const rawTracks = album.tracks
const tracks = ...
return tracks
}
function trackElem(track) {
return `<li>${track.name} - ${track.duration}</li>`
}
const tracks = compose(extractTracks, convertToAlbums)
const tracksFromJson = compose(map(trackElem), tracks)
tracksFromJson(albumJson)
Burritos & other boxes
function Burrito(x) => ({
map: f => Burrito(f(x))
})
function Burrito(x) => ({
map: f => Burrito(f(x))
})
const food = ...
Burrito(food)
.map(f => return peel(f))
.map(f => return chop(f))
.map(f => return cook(f))
///////
// Burrito(cooked(chopped(peeled(food))))
///////
function Burrito(x) => ({
map: f => Burrito(f(x)),
fold: f => f(x)
})
const food = ...
Burrito(food)
.map(x => return peel(x))
.map(x => return chop(x))
.map(x => return cook(x))
.fold(x => return grill(x))
///////
// grilled(cooked(chopped(peeled(food))))
///////
Burrito we all know
const hours = [
'2017-03-31T10:41:47.707Z',
'2017-03-31T10:41:47.707Z',
'2017-03-31T10:41:47.707Z'
]
hours
.map(h => moment.utc(h))
.map(m => new Date(
m.year(),
m.month(),
m.date(),
m.hour(),
m.minutes()
))
And I say MAYBE
const find = (list, predicate) => {
for (let i = 0; i < list.length; ++i) {
const item = list[i]
if (predicate(item)) {
return item
}
}
return null
}
const render = (text) => (`<div>That one is AWESOME ${text}!!!</div>`)
const langs = ['JS', 'Haskell', 'Scala']
const js = find(langs, l => return l.length === 2)
if (js !== null) {
return '<div>hmm</div>'
} else {
render(js)
}
//OR
const render = (text) => {
if(text == null) {
return '<div>hmm</div>'
} else {
return `<div>That one is AWESOME ${text}!!!</div>`
}
}
const js = find(langs, l => return l === 'JS')
render(js)
const find = (list, predicate) => {
for (let i = 0; i < list.length; ++i) {
const item = list[i]
if (predicate(item)) {
return item
}
}
return null
}
const find = (list, predicate) => {
for (let i = 0; i < list.length; ++i) {
const item = list[i]
if (predicate(item)) {
return Maybe.Just(item)
}
}
return Maybe.Nothing()
}
and I say MAYBE
const render = (text) => (`<div>That one is AWESOME ${text}!!!</div>`)
const langs = ['JS', 'Haskell', 'Scala']
const best = find(langs, l => return l === 'JS')
best
.map(render)
.getOrElse('<div>hmm</div>)
Don’t try too hard
const tripIds = reduce(trips, (acc, t) => {
acc.push(t._id)
return acc
}, [])
const tripsIds = map(trips, t => t._id)
const tripsIds = map(trips, ’_id’)
OR
If you have a hammer,
everything looks like a
nail.
Real life fluff
function createTimeslots(trip) {
const slots = _.reduce(trip.days, (acc, d) => {
let [startHour, startMinutes] = _.map(d.startHour.split(':'), i =>
parseInt(i, 10))
startMinutes = normalizeStartMinutes(startMinutes)
const start = ...
const [endHour, endMinutes] = _.map(d.endHour.split(':'), i =>
parseInt(i, 10))
const end = ...
/// GIVE ME MORE
return acc
}, [])
return slots
}
function createTimeslots(trip) {
///GIVE ME MORE
}
function createTimeslotsFromTrips(trips) {
return map(trips, createTimeslots).flatten()
}
function createTimeslotsFromTrips(trips) {
return flatMap(trips, createTimeslots)
}
function createTimeslotsFromTrips(trips) {
return flatMap(createTimeslots, trips)
}
function createTimeslotsFromTrips(trips) {
return flatMap(createTimeslots)(trips)
}
const createTimeslotsFromTrips = _.flatMap(createTimeslots)
Bartek Witczak
@bartekwitczak
bartek@dayone.pl

Functional JS for everyone - 4Developers

  • 1.
  • 3.
  • 4.
  • 5.
  • 6.
    function youngerThan(limit, age){ return age < limit }
  • 7.
    function youngerThan(limit) { returnfunction(age) { return age < limit } }
  • 8.
    function youngerThan(limit) { returnfunction(age) { return age < limit } } const youngerThanMe = youngerThan(28) ... people.map(youngerThanMe)
  • 9.
    In programming languagedesign, a first-class citizen (…) is an entity which supports all the operations generally available to other entities. These operations typically include being passed as an argument, returned from a function, and assigned to a variable
  • 10.
    let languages =['JavaScript', 'Scala', 'Go'] ... ... ... ... let divs = [] for(let i = 0; i < languages.length; i++) { divs.push(`<div>${langs[i]}</div>`) }
  • 11.
    let languages =['JavaScript', 'Scala', 'Go'] languages.push('Java') languages.push('Ruby') languages.splice(0, 3) let divs = [] for(let i = 0; i < languages.length; i++) { divs.push(`<div>${languages[i]}</div>`) }
  • 12.
    const languages =Immutable.List.of( ‘JavaScript', ‘Scala', ‘Go' ) ... ... let divs = [] for(let i = 0; i < languages.length; i++) { divs.push(`<div>${languages[i]}</div>`) }
  • 13.
    • always evaluatesto same result for the same input • evaluation does not cause any semantically observable side effects Pure function
  • 14.
    let user ={...} function createMessage(text) { const message = Db.save(text) return message } function sendMessage(text) { const message = createMessage(text) const messageId = MessageService.send(message, user) return messageId } IMPURE
  • 15.
    function createMessage(Db, text){ const message = Db.save(text) return message } function sendMessage(Db, MessageService, text, user) { const message = createMessage(Db, text) const messageId = MessageService.send(message, user) return messageId } PURE
  • 16.
    function createMessage(Db, text){ const message = Db.save(text) return message } function sendMessage(Db, MessageService, text, user) { const message = createMessage(Db, text) const messageId = MessageService.send(message, user) return messageId } Explicit dependency / Self-documenting
  • 17.
    Testable function createMessage(Db, text){ const message = Db.save(text) return message } function sendMessage(Db, MessageService, text, user) { const message = createMessage(Db, text) const messageId = MessageService.send(message, user) return messageId }
  • 18.
    Resonable function createMessage(Db, text){ const message = Db.save(text) return message } function sendMessage(Db, MessageService, text, user) { const message = createMessage(Db, text) const messageId = MessageService.send(message, user) return messageId }
  • 19.
    The problem withobject-oriented languages is they’ve got all this implicit environment that they carry around with them. You wanted a banana but what you got was a gorilla holding the banana... and the entire jungle Joe Armstrong, Erlang creator
  • 20.
  • 21.
    f ( x,y ) === f ( x ) ( y )
  • 22.
    function add(x, y){ return x + y } add(2, 3) function curried_add (x) { return function (y) { return x + y } } curried_add(2)(3)
  • 23.
    const increment =curried_add(1) increment(5) peopleAge.map(increment) const add10 = curried_add(10) add10(15)
  • 25.
    export const create= (fetch) => { return { ... post: (url, data, token) => { const secureHeader = token ? { 'SECURE-TOKEN': token } : {} return Q(fetch(url, { method: 'post', headers: _.assign({ 'Accept': 'application/json', 'Content-Type': 'application/json' }, secureHeader), body: JSON.stringify(data) })) } ... } export default create(fetch)
  • 26.
    const token ='1jflann24kh11;2114fanakf' http.post(someURL, { user: 'Henry' }, token)
  • 27.
    return { ... post: (token,url, data) => { ... post(token)(url)(data) currying
  • 28.
    const createSecured =(http, token) => { ... post: http.post(token) ... } sercureHttp.post(someURL)({ user: 'Henry' })
  • 29.
  • 30.
    const languages =['JavaScript', 'Scala', 'Haskell'] const divs = [] for(let i = 0; i < languages.length; i++) { divs.push(`<div>${languages[i]}</div>`) } const languages = ['JavaScript', 'Scala', 'Haskell'] const divs = languages.map(l => `<div>${l}</div>`)
  • 31.
    const dimensions =[100, 231, 84] const sum = 0 for(let i = 0; i < dimensions.length; i++) { sum += dimensions[i] } const dimensions = [100, 231, 84] const sum = dimensions.reduce((a, b) => a + b, 0)
  • 32.
    const people =[{name: 'Henry', age: 21}, ... ] const adults = [] for(let i = 0; i < people.length; i++) { if(people[i].age >= 18) { adults.push(people[i]) } } const people = [{name: 'Henry', age: 21}, ... ] const adults = people.filter(p => p.age >= 18)
  • 34.
  • 35.
    f(g(x)) === (f∘ g)(x)
  • 36.
    function compose(f, g){ return function(x) { return f(g(x)) } }
  • 37.
    const add2 =(x) => x + 2 const multiplyBy2 = (x) => x * 2 multiplyBy2(add2(3)) const add2ThenMultiplyBy2 = compose(multiplyBy2, add2) add2ThenMultiplyBy2(3)
  • 38.
    function convertToAlbums(json) { constartist = extractArtist(json) const album = ... return album } function extractTracks(album) { const rawTracks = album.tracks const tracks = ... return tracks } function trackElem(track) { return `<li>${track.name} - ${track.duration}</li>` } const tracks = compose(extractTracks, convertToAlbums) const tracksFromJson = compose(map(trackElem), tracks) tracksFromJson(albumJson)
  • 40.
  • 41.
    function Burrito(x) =>({ map: f => Burrito(f(x)) })
  • 42.
    function Burrito(x) =>({ map: f => Burrito(f(x)) }) const food = ... Burrito(food) .map(f => return peel(f)) .map(f => return chop(f)) .map(f => return cook(f)) /////// // Burrito(cooked(chopped(peeled(food)))) ///////
  • 43.
    function Burrito(x) =>({ map: f => Burrito(f(x)), fold: f => f(x) }) const food = ... Burrito(food) .map(x => return peel(x)) .map(x => return chop(x)) .map(x => return cook(x)) .fold(x => return grill(x)) /////// // grilled(cooked(chopped(peeled(food)))) ///////
  • 44.
    Burrito we allknow const hours = [ '2017-03-31T10:41:47.707Z', '2017-03-31T10:41:47.707Z', '2017-03-31T10:41:47.707Z' ] hours .map(h => moment.utc(h)) .map(m => new Date( m.year(), m.month(), m.date(), m.hour(), m.minutes() ))
  • 45.
    And I sayMAYBE
  • 46.
    const find =(list, predicate) => { for (let i = 0; i < list.length; ++i) { const item = list[i] if (predicate(item)) { return item } } return null }
  • 47.
    const render =(text) => (`<div>That one is AWESOME ${text}!!!</div>`) const langs = ['JS', 'Haskell', 'Scala'] const js = find(langs, l => return l.length === 2) if (js !== null) { return '<div>hmm</div>' } else { render(js) } //OR const render = (text) => { if(text == null) { return '<div>hmm</div>' } else { return `<div>That one is AWESOME ${text}!!!</div>` } } const js = find(langs, l => return l === 'JS') render(js)
  • 48.
    const find =(list, predicate) => { for (let i = 0; i < list.length; ++i) { const item = list[i] if (predicate(item)) { return item } } return null } const find = (list, predicate) => { for (let i = 0; i < list.length; ++i) { const item = list[i] if (predicate(item)) { return Maybe.Just(item) } } return Maybe.Nothing() } and I say MAYBE
  • 49.
    const render =(text) => (`<div>That one is AWESOME ${text}!!!</div>`) const langs = ['JS', 'Haskell', 'Scala'] const best = find(langs, l => return l === 'JS') best .map(render) .getOrElse('<div>hmm</div>)
  • 50.
  • 51.
    const tripIds =reduce(trips, (acc, t) => { acc.push(t._id) return acc }, [])
  • 52.
    const tripsIds =map(trips, t => t._id) const tripsIds = map(trips, ’_id’) OR
  • 53.
    If you havea hammer, everything looks like a nail.
  • 54.
  • 55.
    function createTimeslots(trip) { constslots = _.reduce(trip.days, (acc, d) => { let [startHour, startMinutes] = _.map(d.startHour.split(':'), i => parseInt(i, 10)) startMinutes = normalizeStartMinutes(startMinutes) const start = ... const [endHour, endMinutes] = _.map(d.endHour.split(':'), i => parseInt(i, 10)) const end = ... /// GIVE ME MORE return acc }, []) return slots }
  • 56.
    function createTimeslots(trip) { ///GIVEME MORE } function createTimeslotsFromTrips(trips) { return map(trips, createTimeslots).flatten() } function createTimeslotsFromTrips(trips) { return flatMap(trips, createTimeslots) } function createTimeslotsFromTrips(trips) { return flatMap(createTimeslots, trips) } function createTimeslotsFromTrips(trips) { return flatMap(createTimeslots)(trips) }
  • 57.
    const createTimeslotsFromTrips =_.flatMap(createTimeslots)
  • 59.