Fun* JavaScript Workshop

* Functional

Dr. Gleb Bahmutov PhD

Coding in JS

JavaScript == table

JavaScript < table

We use JS because it is everywhere

  • browser

  • server

  • mobile

  • robots

Dr. Gleb Bahmutov PhD

Node, C++, C#,

Angular, Vue.js, React, server-side etc.

work: Kensho

event analysis and statistics for financial markets

Boston and New York

Problem: code complexity is quickly growing

number of engineers

lifetime of the company

How do I write great apps?

simplicity & clarity

If I can read the source code

and understand what happens

then most likely it will work

const todos = getTodos()
const updatedTodos = addNewTodo(todos, newTodo)
saveTodos(updatedTodos)

where does it get the data?

how does it save the data?

is this object modified?

const todos = getTodos()
const updatedTodos = addNewTodo(todos, newTodo)
saveTodos(updatedTodos)

what if getTodos() is called again

while the first save is still executing?

const todos = getTodos()
const updatedTodos = addNewTodo(todos, newTodo)
saveTodos(updatedTodos)

functional: A way to write your program in terms of simple functions

functions everywhere

We need JavaScript to

  • be as clear as writing an English sentence (or clearer)
  • produce expected result
  • be simple to test
  • assemble complex logic from very simple parts

Everyone has their own path to JS

This workshop

  • imperative code

  • pure functions

  • partial application

  • iteration

  • currying

  • partial application revisited

  • library Ramda

  • async code

  • pure React code

Fun 1

var numbers = [3, 2, 8]
var constant = 2
// 4 16

Problem: given an array of numbers, multiply even values by a constant and print the result

Write a solution using a for loop

Imperative solution

var numbers = [3, 2, 8]
var constant = 2
var k = 0
for (k = 0; k < numbers.length; k += 1) {
  if (numbers[k] % 2 === 0) {
    console.log(numbers[k] * constant)
  }
}

We command the computer to iterate through the list of numbers

What does this code do?

var numbers = [3, 2, 8]
var constant = 2
var k = 0
for (k = 0; k < numbers.length; k += 1) {
  if (numbers[k] % 2 === 0) {
    console.log(numbers[k] * constant)
  }
}

Too many moving parts

var numbers = [3, 2, 8]
var constant = 2
var k = 0
for (k = 0; k < numbers.length; k += 1) {
  if (numbers[k] % 2 === 0) {
    console.log(numbers[k] * constant)
  }
}

for loop at some point will betray you

Your brain is limited to 3 - 7 items in short memory

4 actions (not counting the variables!)

var numbers = [3, 2, 8]
var constant = 2
var k = 0
for (k = 0; k < numbers.length; k += 1) {
  if (numbers[k] % 2 === 0) {
    console.log(numbers[k] * constant)
  }
}

simple functions

a function does one thing only

can be described without using "AND", "OR"

Fun 2

var k = 0
for (k = 0; k < numbers.length; k += 1) {
  if (numbers[k] % 2 === 0) {
    console.log(numbers[k] * constant)
  }
}

Problem: extract functions that can be described with a single sentence

function mul(a, b) {
  return a * b
}
function print(x) {
  console.log(x)
}
function isEven(x) {
  return x % 2 === 0
}
for (k = 0; k < numbers.length; k += 1) {
  if (isEven(numbers[k])) {
    print(mul(numbers[k], constant))
  }
}

multiplies two numbers

prints one argument

returns true if x is even

var k = 0
for (k = 0; k < numbers.length; k += 1) {
  if (numbers[k] % 2 === 0) {
    console.log(numbers[k] * constant)
  }
}
function mul(a, b) {
  return a * b
}
function print(x) {
  console.log(x)
}
function isEven(x) {
  return x % 2 === 0
}
for (k = 0; k < numbers.length; k += 1) {
  if (isEven(numbers[k])) {
    print(mul(numbers[k], constant))
  }
}

Hmm, did we just blow up the size of the code for no reason?

Atomization

initial spaghetti code

mul
print
isEven
...

short readable code

"atoms"

compare: multiply even numbers by a constant

if (numbers[k] % 2 === 0) {
  console.log(numbers[k] * constant)
}
if (isEven(numbers[k])) {
  print(mul(numbers[k], constant))
}

almost reads like the problem itself

Fun 3

function mul(a, b) {
  return a * b
}
function print(x) {
  console.log(x)
}
function isEven(x) {
  return x % 2 === 0
}

Problem: which function is NOT like the other two?

hint: look where the variables are coming from

Fun 3

function mul(a, b) {
  return a * b
}
function print(x) {
  console.log(x)
}
function isEven(x) {
  return x % 2 === 0
}

Problem: which function is NOT like the other two?

Pure functions

function mul(a, b) {
  return a * b
}

only uses its arguments

same arguments => same result

does not modify the outside environment

Impure functions

function print(x) {
  console.log(x)
}

uses outside variable

modifies the outside environment

Pure functions are the best

Simple to write

Simple to read

Simple to test

Lexical scope

function mulBy(K) {
  return function (x) {
    return K * x
  }
}

Look at the source code outwards

Higher order functions

function mulBy(K) {
  return function (x) {
    return K * x
  }
}

A function returns another function

Pure function can return non-pure function

function mulBy(K) {
  return function (x) {
    return K * x
  }
}

pure

impure

Function's source code

function mulBy(K) {
  return function (x) {
    return K * x
  }
}
const double = mulBy(2)
console.log(double.toString())
// function (x) {
//   return K * x
// }

Function's source code

function mulBy(K) {
  return function (x) {
    return K * x
  }
}
const double = mulBy(2)
/*
double is same as
K = 2
function (x) {
  return K * x
}
*/

Fun 4

function mul(a, b) {
  return a * b
}
const constant = 2
for (...) {
  mul(constant, numbers[k]))
}

Use known values early

function and one argument are known early

Fun 4

function mul(a, b) {
  return a * b
}
function mulBy = /* something here */;
var constant = 2
var byConstant = mulBy(constant)
for (...) {
  byConstant(numbers[k])
}

problem: make a function that "premultiplies" by a constant

Return a function

function mul(a, b) {
  return a * b
}
function mulBy(K) {
  return function(x) {
    return mul(K, x)
  }
}
const constant = 2
const byConstant = mulBy(constant)
for (...) {
  byConstant(numbers[k])
}

Arrow function

function mul(a, b) {
  return a * b
}
const mulBy = K => x => mul(K, x)
const constant = 2
const byConstant = mulBy(constant)
for (...) {
  byConstant(numbers[k])
}

Reuse existing functions

function mul(a, b) {
  return a * b
}
const mulBy = K => x => mul(K, x)
const constant = 2
const byConstant = mulBy(constant)
for (...) {
  byConstant(numbers[k])
}

Referential transparency

function mul(a, b) {
  return a * b
}
const mulBy = K => x => K * x
const constant = 2
const byConstant = mulBy(constant)
for (...) {
  byConstant(numbers[k])
}

Fun 5

const mulBy = K => x => K * x
const double = mulBy(2)
console.log(double.toString())
// x => K * x

problem: change the value of K

hint: "eval" uses current lexical scope

// >>> insert statement here <<<
const triple 
  = eval('(' + double.toString() + ')')
console.log(triple(10))
// 30

Fun 5

K = 3
// eval('(' + double.toString() + ')')
const triple = eval('(x => K * x)')
console.log(triple(10))
// 30

Faking lexical scope

Fun 6

function mul(a, b) {
  return a * b
}
const constant = 2
const byConstant = /* something here */

problem: bind known argument value without writing new function

hint: look at Function.prototype.bind

Fun 6

function mul(a, b) {
  return a * b
}
const constant = 2
const byConstant = mul.bind(null, constant)
// byConstant(10) = 20
// byConstant(-1) = -2

Partial application

function mul(a, b) {
  return a * b
}
const constant = 2
const byConstant = mul.bind(null, constant)

Pure functions - pass everything via arguments - a lot of arguments

Left to right application makes shorter functions from known arguments

Function signature design

Place on the left arguments most likely to be known early

function savePassword(userId, newPassword)
// vs
function savePassword(newPassword, userId)
function savePassword(userId, newPassword) {...}
function onLogin(userId) {
  return {
    savePassword: savePassword.bind(null, userId),
    deleteAccount: ...
  }
}
function mul(a, b) {
  return a * b
}
function print(x) {
  console.log(x)
}
function isEven(x) {
  return x % 2 === 0
}
const byConstant = mul.bind(null, constant)
var k
for (k = 0; k < numbers.length; k += 1) {
  if (isEven(numbers[k])) {
    print(byConstant(numbers[k]))
  }
}

fn

fn

data

for (k = 0; k < numbers.length; k += 1) {
  if (isEven(numbers[k])) {
    print(byConstant(numbers[k]))
  }
}

fn

fn

data

Fun 7

problem: write function that executes `f(g(x))`

mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])

Fun 7

my coding process

mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])

"compose" is a function

mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])
function compose()

that takes two arguments

mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])
function compose(f, g)

and returns another function

mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])
function compose(f, g) {
  return function() {
  
  }
}

that expects a single argument

mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])
function compose(f, g) {
  return function(x) {
  
  }
}

and applies original functions to it

mulAndPrint = compose(print, byConstant)
mulAndPrint(numbers[k])
function compose(f, g) {
  return function(x) {
    return f(g(x))
  }
}
mulAndPrint = compose(print, byConstant)
for (k = 0; k < numbers.length; k += 1) {
  if (isEven(numbers[k])) {
    mulAndPrint(numbers[k])
  }
}

fn

data

Composition

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

Note: since JavaScript functions return single result, it is easier to compose unary functions

const F = compose(f, g)
F(x)

pure

pure

Note: composition of pure functions is a pure function

mulAndPrint = compose(print, byConstant)

pure

print "pollutes" the composed function

This workshop

  • imperative code

  • pure functions

  • partial application

  • iteration

  • currying

  • partial application revisited

  • library Ramda

  • async code

  • pure React code

Atomization

initial spaghetti code

mul
print
isEven
...

short readable code

Clumping

.bind, compose

const byConstant = mul.bind(null, constant)
const mulAndPrint = compose(print, byConstant)
var k = 0
for (k = 0; k < numbers.length; k += 1) {
  if (isEven(numbers[k])) {
    mulAndPrint(numbers[k])
  }
}

We still have iteration and side effects mixed with pure functions

var k
for (k = 0; k < list.length; k += 1) {
    // call fn(list[k])
}

Fun 8

problem: write 3 iteration functions

do not use built-in Array methods

var k = 0
for (k = 0; k < numbers.length; k += 1) {
  if (isEven(numbers[k])) {
    print(byConstant(numbers[k]))
  }
}

filter

map

side effect

map(fn, list)

or

map(list, fn)?

function filter(fn, list) {
  var k = 0
  const result = []
  for (k = 0; k < list.length; k += 1) {
    if (fn(list[k])) {
      result.push(list[k])
    }
  }
  return result
}
// filter(isEven, [1, 2, 3])
// [2]

filter

callback first

filter(isEven, [1, 2, 3])

filter

Callback function is likely to be known right away

evenNumbers = filter.bind(null, isEven)
evenNumbers([1, 2, 3])
// [2]
evenNumbers = filter.bind(null, isEven)
evenNumbers([1, 2, 3])
// [2]

Declarative style

We got the filtered numbers "somehow" by providing a predicate

function map(fn, list) {
  var k = 0
  const result = []
  for (k = 0; k < list.length; k += 1) {
    result.push(fn(list[k]))
  }
  return result
}
// map(byConstant, [1, 2, 3])
// [2, 4, 6]

map

map(byConstant, numbers)

map

Callback function is known right away

multiplyAll = map.bind(null, byConstant)
multiplyAll([1, 2, 3])
// [2, 4, 6]
function forEach(fn, list) {
  var k = 0
  for (k = 0; k < list.length; k += 1) {
    fn(list[k])
  }
}
// forEach(print, [1, 2, 3])
// 1 2 3

forEach

forEach is for "dirty" actions

forEach(print, map(byConstant, filter(isEven, numbers)))

Partial application FTW

const onlyEven = filter.bind(null, isEven)
forEach(print, map(byConstant, onlyEven(numbers)))
const onlyEven = filter.bind(null, isEven)
const multiply = map.bind(null, byConstant)
forEach(print, multiply(onlyEven(numbers)))
const onlyEven = filter.bind(null, isEven)
const multiply = map.bind(null, byConstant)
const printAll = forEach.bind(null, print)
printAll(multiply(onlyEven(numbers)))

Fun 9

const onlyEven = filter.bind(null, isEven)
const multiply = map.bind(null, byConstant)
const printAll = forEach.bind(null, print)
printAll(multiply(onlyEven(numbers)))

problem: write compose for 3 functions using arrow function and collapse the above code

const onlyEven = filter.bind(null, isEven)
const multiply = map.bind(null, byConstant)
const printAll = forEach.bind(null, print)
const compose = (f, g, h) => x => f(g(h(x)))
const solution = compose(printAll, multiply, onlyEven)
solution(numbers)

fn

data

const onlyEven = filter.bind(null, isEven)
const multiply = map.bind(null, byConstant)
const printAll = forEach.bind(null, print)
const compose = (f, g, h) => x => f(g(h(x)))
const solution = compose(printAll, multiply, onlyEven)
solution(numbers)

Fun 10

problem: make partial application in {filter, map, forEach} built-in

ugly!

// filter is a binary function
const onlyEven = filter(isEven)
onlyEven([1, 2, 3])
// [2]

simple: add custom code to each function

function map(fn) {
  return function(list) {
    var k = 0
    const result = []
    for (k = 0; k < list.length; k += 1) {
      result.push(fn(list[k]))
    }
    return result
  }
}
const multiply = map(byConstant)
multiply([1, 2, 3])
const onlyEven = filter(isEven)
const multiply = map(byConstant)
const printAll = forEach(print)
const compose = (f, g, h) => x => f(g(h(x)))
const solution = compose(printAll, multiply, onlyEven)
solution(numbers)

I don't like reading the composed function

const onlyEven = filter(isEven)
const multiply = map(byConstant)
const printAll = forEach(print)
const compose = (f, g, h) => x => f(g(h(x)))
const solution = compose(printAll, multiply, onlyEven)
solution(numbers)

Fun 11

problem: make it read like the original English description

multiply even numbers by a constant and print the result

const onlyEven = filter(isEven)
const multiply = map(byConstant)
const printAll = forEach(print)
const pipe = (f, g, h) => x => h(g(f(x)))
const solution = pipe(onlyEven, multiply, printAll)
solution(numbers)

pipe is the reverse of compose

function map(fn, list) { ... }
curry(map)(byConstant)([1, 2, 3])
// [2, 4, 6]

Fun 12

problem: write utility function curry that can convert any binary function into two unary functions

function curry(fn) {
  return function (x) {
    return function (y) {
      return fn(x, y)
    }
  }
}
const curry = fn => x => y => fn(x, y)
const map = curry((fn, list) => {
  var k = 0
  const result = []
  for (k = 0; k < list.length; k += 1){
    result.push(fn(list[k]))
  }
  return result
})
const solution = pipe(
  filter(isEven),
  map(byConstant),
  forEach(print)
)
solution(numbers)

Curry for shorter code

const solution = pipe(
  filter(x => isEven(x)),
  map(y => byConstant(y)),
  forEach(z => print(z))
)
solution(numbers)

Where are variables?

Anonymous arrow functions are redundant

Point-free style

const solution = pipe(
  filter(isEven),
  map(byConstant),
  forEach(print)
)
solution(numbers)

Hard: keeping track of signatures

 
const solution = pipe(
  filter(isEven),
  map(byConstant),
  forEach(print)
)
solution(numbers)
// filter :: fn => [x] => [x]
// [x] => [x]
// [x] => [x]
// [x] => undefined
// solution :: [x] => undefined
// numbers is [x]
// undefined

Fun 13

console.log(['1','2','3'].map(parseFloat))
// 1, 2, 3

problem: why?

console.log(['1','2','3'].map(parseInt))
// 1, NaN, NaN

Signatures

// parseFloat :: string => number
console.log(['1','2','3'].map(parseFloat))
// parseInt :: string, radix => number
console.log(['1','2','3'].map(parseInt))
// 1, NaN, NaN
// Array.prototype.map :: cb
// where cb :: item, index, array

parseInt

// parseInt :: string, radix => number
console.log(['1','2','3'].map(parseInt))
parseInt('1', 0)  // 1
parseInt('2', 1)  // NaN
parseInt('3', 2)  // NaN

Typical solution

// function parseInt(x, radix)
// but Array.map is (value, index, array)
['1', '2', '3'].map(function (x) {
    return parseInt(x)
})

Adapting parseInt

// parseInt :: string, radix => number
unary(parseInt)('2', 1) // 2

problem: write utility "unary" that forces binary function to ignore second argument

['1','2','3'].map(unary(parseInt))
// [1, 2, 3]

unary(parseInt)

// parseInt :: string, radix => number
const unary = f => x => f(x)
// unary(parseInt) :: string => number
['1','2','3'].map(unary(parseInt))
// [1, 2, 3]
// parseInt :: string, radix => number
const parse10 = partialRight(parseInt, 10)

problem: write partial application from the right utility

Fun 14 - Adapting parseInt #2

['1','2','3'].map(parse10)
// [1, 2, 3]

partialRight

const partialRight = (f, b) => a => f(a, b)
// parseInt :: string, radix => number
const parse10 = partialRight(parseInt, 10)
// parse10 :: string => number
['1', '2', '3'].map(parse10)
// 1, 2, 3

This workshop

  • imperative code

  • pure functions

  • partial application

  • iteration

  • currying

  • partial application revisited

  • library Ramda

  • async code

  • pure React code

Pure functions want many arguments. How can we partially apply known ones?

Partial application from the left

function fn(a, b, c) { ... }
var newFn = fn.bind(null, valueA, valueB);
// or
var _ = require('lodash');
var newFn = _.partial(fn, valueA, valueB);
// or
var R = require('ramda');
var newFn = R.partial(fn, valueA, valueB);

Partial application from the right

const base10 = _.partialRight(parseInt, 10)
['1', '2', '3'].map(base10); 
// [1, 2, 3]
// radix is bound, 
// index and array arguments are ignored

Partial application with placeholders

var S = require('spots');
const base10 = S(parseInt, S, 10)
['1', '2', '3'].map(base10); 
// [1, 2, 3]

Partial application by name

// divide by 10
function divide(a, b) { return a / b; }
var selective = require('heroin');
var by10 = selective(divide, { b: 10 });
console.log(by10(10)); // 1 (a = 10, b = 10)
console.log(by10(2)); // 0.2 (a = 2, b = 10)

Partial application by key

function fn(options) { ... }
var obind = require('obind');
var withBar = obind(fn, { bar: 'bar' });
withBar({ baz: 'baz' });
/*
equivalent to
foo({
    bar: 'bar',
    baz: 'baz'
})
*/

Libraries

Ramda - simple, pure, curried functions

Ask questions at gitter.im/ramda/ramda

var numbers = [3, 2, 8]
var constant = 2
// 4
// 16

problem: solve the problem using Ramda methods

Fun 15

const R = require('ramda')
var numbers = [3, 2, 8]
var constant = 2
const solution = R.pipe(
  R.filter(isEven),
  R.map(R.multiply(constant)),
  R.forEach(print)
)
solution(numbers)
const R = require('ramda')
var numbers = [3, 2, 8]
var constant = 2
const solution = R.pipe(
  R.filter(isEven),
  R.map(R.multiply(constant)),
  R.forEach(print)
)
solution(numbers)

We only needed 2 tiny custom functions

const R = require('ramda')
var numbers = [3, 2, 8]
var constant = 2
const solution = R.pipe(
  R.filter(isEven),
  R.map(R.multiply(constant)),
  R.forEach(print)
)
solution(numbers)

Passing functions as arguments

6 functions

2 values

Methods usually make poor callbacks (because of "this")

forEach(console.log, [1, 2, 3])

Error: Illegal invocation

forEach(console.log.bind(console), [1, 2, 3])

This workshop

  • imperative code

  • pure functions

  • partial application

  • iteration

  • currying

  • partial application revisited

  • library Ramda

  • async code

  • pure React code

const numbers = () => Promise.resolve([3, 2, 8])

problem: "numbers" is not a known array, it is an asynchronous function

Fun 16 - bonus

hint: look at R.pipeP

const numbers = () => Promise.resolve([3, 2, 8])
const solution = R.pipeP(
  numbers,
  R.filter(isEven),
  R.map(R.multiply(constant)),
  R.forEach(print)
)
solution()
// 4
// 16

Fun 17 - bonus (React in FP)

const Numbers = (numbers) => (
  <ul>
    {R.map(x => (<li>{x}</li>), numbers)}
  </ul>
)
const Container = children => (
  <div className="container">
    <h1>Numbers are</h1>
    {children}
  </div>
)
const App = appState => (
 Container(Numbers(appState.numbers))
)

list iteration

composition

App = composition

const App = appState => (
 Container(Numbers(appState.numbers))
)
// same as
const App = R.compose(
  Container, Numbers, R.prop('numbers')
)

Advice

Writing React code in FP style can use some of the Ramda functions

Better Advice

Using a better library like virtual-dom allows to fully use FP style

Because JSX or Class syntax do not get in the way

Imperative / OOP

vs

Functional

OOP: Every piece of your code is unique

Functional style uses small number of building blocks

To construct apps of any complexity

Advice

Write small pure functions

Adapt and compose

Change one part at a time

Progress through the code

Advice

Read blog post "Getting good at FP"

list of books, tutorials, libraries and people

Next steps

Working with async code

Boxed values (Promise, Task, Maybe, Either monads)

Reactive functional programming

Fun* JavaScript Workshop

Fun* JavaScript Workshop

By Gleb Bahmutov

Fun* JavaScript Workshop

The principles and basics of functional programming in JavaScript using small coding problems. Pure functions, partial application, composition, iteration, lexical scope, Ramda library.

  • 4,019
Loading comments...

More from Gleb Bahmutov