59
Functional JavaScript for everyone Bartek Witczak

Functional JS for everyone - 4Developers

Embed Size (px)

Citation preview

Page 1: Functional JS for everyone - 4Developers

Functional JavaScript for everyone

Bartek Witczak

Page 2: Functional JS for everyone - 4Developers
Page 3: Functional JS for everyone - 4Developers

1/320

http://te.xel.io/posts/2015-02-22-category-theory-and-human-behavior.html

Page 4: Functional JS for everyone - 4Developers

https://prateekvjoshi.com/2014/11/01/functors-in-c/

Page 5: Functional JS for everyone - 4Developers

Functions

Page 6: Functional JS for everyone - 4Developers

function youngerThan(limit, age) { return age < limit }

Page 7: Functional JS for everyone - 4Developers

function youngerThan(limit) { return function(age) { return age < limit } }

Page 8: Functional JS for everyone - 4Developers

function youngerThan(limit) { return function(age) { return age < limit } }

const youngerThanMe = youngerThan(28)

...

people.map(youngerThanMe)

Page 9: Functional JS for everyone - 4Developers

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

Page 10: Functional JS for everyone - 4Developers

let languages = ['JavaScript', 'Scala', 'Go']

...

...

...

...

let divs = [] for(let i = 0; i < languages.length; i++) { divs.push(`<div>${langs[i]}</div>`) }

Page 11: Functional JS for everyone - 4Developers

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>`) }

Page 12: Functional JS for everyone - 4Developers

const languages = Immutable.List.of( ‘JavaScript', ‘Scala', ‘Go' )

...

...

let divs = [] for(let i = 0; i < languages.length; i++) { divs.push(`<div>${languages[i]}</div>`) }

Page 13: Functional JS for everyone - 4Developers

• always evaluates to same result for the same input

• evaluation does not cause any semantically observable side effects

Pure function

Page 14: Functional JS for everyone - 4Developers

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

Page 15: Functional JS for everyone - 4Developers

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

Page 16: Functional JS for everyone - 4Developers

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

Page 17: Functional JS for everyone - 4Developers

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 }

Page 18: Functional JS for everyone - 4Developers

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 }

Page 19: Functional JS for everyone - 4Developers

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

Page 20: Functional JS for everyone - 4Developers

Currying

Page 21: Functional JS for everyone - 4Developers

f ( x, y ) === f ( x ) ( y )

Page 22: Functional JS for everyone - 4Developers

function add(x, y) { return x + y }

add(2, 3)

function curried_add (x) { return function (y) { return x + y } }

curried_add(2)(3)

Page 23: Functional JS for everyone - 4Developers

const increment = curried_add(1)

increment(5)

peopleAge.map(increment)

const add10 = curried_add(10)

add10(15)

Page 24: Functional JS for everyone - 4Developers
Page 25: Functional JS for everyone - 4Developers

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)

Page 26: Functional JS for everyone - 4Developers

const token = '1jflann24kh11;2114fanakf'

http.post(someURL, { user: 'Henry' }, token)

Page 27: Functional JS for everyone - 4Developers

return { ...

post: (token, url, data) => { ...

post(token)(url)(data)

currying

Page 28: Functional JS for everyone - 4Developers

const createSecured = (http, token) => { ...

post: http.post(token)

... }

sercureHttp.post(someURL)({ user: 'Henry' })

Page 29: Functional JS for everyone - 4Developers

No more loops

Page 30: Functional JS for everyone - 4Developers

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>`)

Page 31: Functional JS for everyone - 4Developers

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)

Page 32: Functional JS for everyone - 4Developers

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)

Page 33: Functional JS for everyone - 4Developers
Page 34: Functional JS for everyone - 4Developers

Function composition

Page 35: Functional JS for everyone - 4Developers

f(g(x)) === (f ∘ g)(x)

Page 36: Functional JS for everyone - 4Developers

function compose(f, g) { return function(x) { return f(g(x)) } }

Page 37: Functional JS for everyone - 4Developers

const add2 = (x) => x + 2 const multiplyBy2 = (x) => x * 2

multiplyBy2(add2(3))

const add2ThenMultiplyBy2 = compose(multiplyBy2, add2) add2ThenMultiplyBy2(3)

Page 38: Functional JS for everyone - 4Developers

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)

Page 39: Functional JS for everyone - 4Developers
Page 40: Functional JS for everyone - 4Developers

Burritos & other boxes

Page 41: Functional JS for everyone - 4Developers

function Burrito(x) => ({ map: f => Burrito(f(x)) })

Page 42: Functional JS for everyone - 4Developers

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)))) ///////

Page 43: Functional JS for everyone - 4Developers

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)))) ///////

Page 44: Functional JS for everyone - 4Developers

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() ))

Page 45: Functional JS for everyone - 4Developers

And I say MAYBE

Page 46: Functional JS for everyone - 4Developers

const find = (list, predicate) => { for (let i = 0; i < list.length; ++i) { const item = list[i] if (predicate(item)) { return item } } return null }

Page 47: Functional JS for everyone - 4Developers

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)

Page 48: Functional JS for everyone - 4Developers

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

Page 49: Functional JS for everyone - 4Developers

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>)

Page 50: Functional JS for everyone - 4Developers

Don’t try too hard

Page 51: Functional JS for everyone - 4Developers

const tripIds = reduce(trips, (acc, t) => { acc.push(t._id) return acc }, [])

Page 52: Functional JS for everyone - 4Developers

const tripsIds = map(trips, t => t._id)

const tripsIds = map(trips, ’_id’)

OR

Page 53: Functional JS for everyone - 4Developers

If you have a hammer, everything looks like a

nail.

Page 54: Functional JS for everyone - 4Developers

Real life fluff

Page 55: Functional JS for everyone - 4Developers

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 }

Page 56: Functional JS for everyone - 4Developers

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) }

Page 57: Functional JS for everyone - 4Developers

const createTimeslotsFromTrips = _.flatMap(createTimeslots)

Page 58: Functional JS for everyone - 4Developers
Page 59: Functional JS for everyone - 4Developers

Bartek Witczak

@bartekwitczak

[email protected]