Introduction to Functional
Programming in JavaScript
Boris Burdiliak @ WebUP
Boris Burdiliak
@borisburdiliak
github.com/bburdiliak
linkedin.com/in/borisburdiliak/
Agenda
● are features of JS suitable for FP
● pure functions
● functional toolbox
● how to improve your code now
General Programming Principles
● DRY
● YAGNI
● KISS
● loose coupling
● high cohesion
● the principle of least surprise
● single responsibility
Headaches
● mutable state
● unrestricted side effects
● unprincipled design
Javascript - important features for FP
HAS
Anonymous functions and concise
lambda syntax
Closures
Higher-order functions
First class functions
DOES NOT HAVE
Purity
Immutability
Recursion
Concise lambda syntax
lambda expressions are abstractions which enable a function to be passed around like
data (value)
ES6 fat arrow
(x, y) => x * y
Closures
A closure is the combination of a function bundled together (enclosed) with
references to its surrounding state (the lexical environment).
Higher order functions
functions as
● function parameters
● return value of a function
First Class Functions
constants
assignment to variables
values of keys of an object
array items
function parameters
return value of a function
First Class Functions - use their power
layers of indirection with no added value
const fullname = user => `${user.firstName} ${user.lastName}`
const fullnames = users.map(user => fullname(user))
const fullnames = users.map(fullname)
First Class Functions
const getUsers = callback => ajaxCall(json => callback(json));
First Class Functions
// ignorant
const getUsers = callback => ajaxCall(json => callback(json));
const getUsers = callback => ajaxCall(callback);
First Class Functions
// ignorant
const getUsers = callback => ajaxCall(json => callback(json));
const getUsers = callback => ajaxCall(callback);
// enlightened
const getUsers = ajaxCall;
First Class Functions - help with
layers of indirection with no added value
> amount of redundant code to maintain and search through
name and reference arguments
First Class Functions - Naming
// specific to our current blog
const validArticles = articles =>
articles.filter(article => article !== null && article !== undefined)
// vastly more relevant for future projects
const compact = xs => xs.filter(x => x !== null && x !== undefined)
Pure Functions
A pure function is a function where the return value is only determined by its input
values, without observable side effects.
Pure Functions - Example
let G = 6.67408
const force = (m1, m2, r) => G * m1 * m2 / (r * r)
Pure Functions - Example
let G = 6.67408
const force = (m1, m2, r) => G * m1 * m2 / (r * r)
❌
Pure Functions - Example
const trace = (title, text) => console.log(title, text)
Pure Functions - Example
const trace = (title, text) => console.log(title, text)
❌
Pure Functions - Example
const max = (m1, m2) => m1 > m2 ? m1 : m2
Pure Functions - Example
const max = (m1, m2) => m1 > m2 ? m1 : m2
✅
Pure Functions - Example
const oneDayLater = () => {
const today = moment();
return moment(today).add(1, 'days');
}
Pure Functions - Example
const oneDayLater = () => {
const today = moment();
return moment(today).add(1, 'days');
}
❌
Pure Functions - Example
const cached = memoize((a) => a*2)
Pure Functions - Example
const cached = memoize((a) => a*2)
❌✅
Pure Functions - Example
const signUp = (attrs) => {
const user = saveUser(attrs);
welcomeUser(user);
};
Pure Functions - Example
const signUp = (attrs) => {
const user = saveUser(attrs);
welcomeUser(user);
};
❌
Pure Functions - Example
const signUp = (Db, Email, attrs) => {
const user = saveUser(Db, attrs);
welcomeUser(Email, user);
};
Pure Functions - Example
const signUp = (Db, Email, attrs) => {
const user = saveUser(Db, attrs);
welcomeUser(Email, user);
};
❌
Side Effects
changing the file system
inserting a record into a database
making an http call
mutations
printing to the screen / logging
obtaining user input
querying the DOM
accessing system state
Pure Functions - Example
const signUp = (Db, Email, attrs) => () => {
const user = saveUser(Db, attrs);
welcomeUser(Email, user);
};
❌✅
Benefits of Pure Functions
Cacheable
const memoize = (f) => {
const cache = {};
return (...args) => {
const argStr = JSON.stringify(args);
cache[argStr] = cache[argStr] || f(...args);
return cache[argStr];
};
};
const computation = memoize(costlyComputation)
Self-documenting
explicit function dependencies
just from its signature, we know what it will use
forced to "inject" dependencies - more flexible - parametrized
Portable
pure functions
serialize
send
run anywhere
imperative programming methods
tangled with state
dependencies
effects
You wanted a banana but what
you got was a gorilla holding
the banana... and the entire
jungle.
Joe Armstrong, Erlang creator
Testable
give input and assert output
Offer Referential Transparency
In functional programming, referential transparency is generally defined as the fact
that an expression, in a program, may be replaced by its value (or anything having the
same value) without changing the result of the program.
Easy to reason about
no need to search for context
everything depends just on the input arguments
Parallelism
no need to access shared memory
no race conditions caused by side effects
Our Functional Toolbox
filter, map, reduce
const findEven = (numbers) => {
const even = []
numbers.forEach(number => {
if (isEven(number)) {
even.push(number)
}
})
return even;
}
const isEven = (number) => number % 2 === 0
const findEven = numbers => numbers.filter(isEven)
Curry
Curry
A curried function is a function that takes multiple arguments one at a time.
Call this function with one argument and it returns a function that takes the
remaining arguments
const sum = (x, y) => x + y;
const sum = x => y => x + y;
const increment = sum(1)
const add10 = sum(10)
Curry base
const match = curry((what, s) => s.match(what));
const replace = curry((what, replacement, s) => s.replace(what, replacement));
const filter = curry((f, xs) => xs.filter(f));
const map = curry((f, xs) => xs.map(f));
match(/r/g, 'hello world'); // [ 'r' ]
const hasLetterR = match(/r/g); // x => x.match(/r/g)
hasLetterR('hello world'); // [ 'r' ]
hasLetterR('just j and s and t etc'); // null
filter(hasLetterR, ['rock and roll', 'smooth jazz']); // ['rock and roll']
const removeStringsWithoutRs = filter(hasLetterR); // xs => xs.filter(x => x.match(/r/g))
removeStringsWithoutRs(['rock and roll', 'smooth jazz', 'drum circle']); // ['rock and roll', 'drum
circle']
const noVowels = replace(/[aeiou]/ig); // (r,x) => x.replace(/[aeiou]/ig, r)
const censored = noVowels('*'); // x => x.replace(/[aeiou]/ig, '*')
censored('Chocolate Rain'); // 'Ch*c*l*t* R**n'
Curry - positioning of arguments
iteratee-first
data-last
preload data / dependencies
const findEven = numbers => filter(isEven, numbers)
// apply currying to get pointfree version
const findEven = filter(isEven)
Real-world examples of currying
export default connect(mapStateToProps)(EventsComponent)
const handleChange = (fieldName) => (event) => {
saveField(fieldName, event.target.value)
}
<input type="text" onChange={handleChange('email')} />
React and Redux
Event Handling
Compose
Functional composition
Functional composition
const compose = (f, g) => x => f(g(x));
const toUpperCase = x => x.toUpperCase();
const exclaim = x => `${x}!`;
const shout = compose(exclaim, toUpperCase);
shout('send in the clowns'); // "SEND IN THE CLOWNS!"
const shout = x => exclaim(toUpperCase(x));
Associativity of Functional composition
compose(f, compose(g, h)) === compose(compose(f, g), h);
compose(toUpper, compose(head, reverse))
===
compose(compose(toUpper, head), reverse);
const lastUpper = compose(toUpper, head, reverse);
with variadic compose
Composition in real world
const withEditMode = compose(
withState('editMode', 'setEditMode', false),
withHandlers({
toggleEditMode: ({ setEditMode }) => () => {
setEditMode((editMode) => !editMode);
},
}),
);
export default compose(
withEditMode,
withModal,
withStyles(styles),
)(EventManagement);
Hooks!
const EditButton = () => {
const [editMode, setEditMode] = useState(0);
return (
<div>
<p>Edit mode is {String(editMode)}</p>
<button onClick={() => setEditMode(R.not)}>
Click me
</button>
</div>
);
}
Start today!
coding pure functions
cooking good curry
composing functions
embracing pointfree programming
using hooks
using ramda
References
https://mostly-adequate.gitbooks.io/mostly-adequate-guide
https://medium.com/javascript-scene/why-learn-functional-programming-in-javascript-composing-software-ea13afc7a257
https://blog.benestudio.co/currying-in-javascript-es6-540d2ad09400
https://egghead.io/courses/professor-frisby-introduces-composable-functional-javascript
https://alvinalexander.com/scala/fp-book/benefits-of-pure-functions
https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-pure-function-d1c076bec976
https://en.wikipedia.org/wiki/Lambda_calculus
https://medium.freecodecamp.org/discover-functional-programming-in-javascript-with-this-thorough-introduction-a2ad9af2d645
Thanks!Thank you for having me!

Functional programming in javascript

Editor's Notes

  • #8 http://curtclifton.net/papers/MoseleyMarks06a.pdf
  • #9 https://hackernoon.com/javascript-and-functional-programming-pt-2-first-class-functions-4437a1aec217 https://medium.com/javascript-scene/why-learn-functional-programming-in-javascript-composing-software-ea13afc7a257 Purity: In JavaScript, purity must be achieved by convention. If you’re not building most of your application by composing pure functions, you’re not programming using the functional style. It’s unfortunately easy in JavaScript to get off track by accidentally creating and using impure functions. Immutability: In pure functional languages, immutability is often enforced. JavaScript lacks efficient, immutable trie-based data structures used by most functional languages, but there are libraries that help, including Immutable.js and Mori. I’m hoping that future versions of the ECMAScript spec will embrace immutable data structures. Recursion: JavaScript technically supports recursion, but most functional languages have a feature called tail call optimization. Tail call optimization is a feature which allows recursive functions to reuse stack frames for recursive calls. Without tail call optimization, a call stack can grow without bounds and cause a stack overflow. JavaScript technically got a limited form of tail call optimization in the ES6 specification. Unfortunately, only one of the major browser engines implemented it, and the optimization was partially implemented and then subsequently removed from Babel (the most popular standard JavaScript compiler, used to compile ES6 to ES5 for use in older browsers).
  • #11 https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36
  • #13 https://hackernoon.com/javascript-and-functional-programming-pt-2-first-class-functions-4437a1aec217
  • #57 https://medium.com/javascript-scene/curry-and-function-composition-2c208d774983