Upload
bartek-witczak
View
63
Download
2
Embed Size (px)
Citation preview
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)