functional javascript for everyone
TRANSCRIPT
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(age, limit) { return age < limit }
function youngerThan(age) { return function(limit) { return age < limit } }
function youngerThan(age) { return function(limit) { 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)