#+TITLE: Functional Programming in JavaScript #+PROPERTY: :html-toplevel-hlevel 1 #+PROPERTY: :with-toc 0 * Why? Imperative programming is concerned with *how* Functional programming is concerned with *what* * Concepts - First-class Functions - Higher-order Functions - Recursion - Pure functions - Currying & Partial Application * Further concepts - Lazy Evaluation - Types & Data Structures - Category Theory * First-class functions - Have no restriction on their use - Are values - Enable the use of callback functions in JavaScript #+BEGIN_SRC js const fn = function () { return 2 } #+END_SRC * Higher-order functions Functions that operate on other functions are higher-order functions #+BEGIN_SRC js const succ = function (x) { return x + 1 } const arr = [1, 2, 3, 4] arr.map(succ) #+END_SRC Here, =Array.prototype.map= is the higher-order function * Higher-order functions (cont.) Functions that return functions are also higher-order functions #+BEGIN_SRC js function adder (n) { return function (x) { return n + x } } const add1 = adder(1) #+END_SRC =adder= is a higher-order function * Pure functions Functions without side-effects #+BEGIN_SRC js const succ = (x) => x + 1 console.log(succ(succ(1))) // could be optimised away by a compiler, e.g.: console.log(3) #+END_SRC * Recursion Functions that call themselves #+BEGIN_SRC js function fibonacci (n) { switch (n) { case 0: case 1: return 1 default: return fibonacci(n - 1) + fibonacci(n - 2) } } #+END_SRC * Partial application The infamous =Function.prototype.bind= in JavaScript #+BEGIN_SRC js function add (x, y) { return x + y } const add1 = add.bind(add, 1) add1(3) // = 4 #+END_SRC * Partial application (cont.) After ES6 introduced arrow functions, partial application has become more popular #+BEGIN_SRC js const add = x => y => x + y #+END_SRC * Currying Related to partial application, but more implicit and general Translates */1/ function of arity /n/* to */n/ functions of arity /1/* #+BEGIN_SRC js function volume (w, d, h) { return w * d * h } const vol = curry(volume) vol(10)(20)(30) // is strictly equivalent to volume(10, 20, 30) #+END_SRC * Easy Currying In order to make currying (and partial application) easier to use, move the *most important* argument to a function to the end: #+BEGIN_SRC js const badMap = (arr, fn) => arr.map(fn) const goodMap = (fn, arr) => arr.map(fn) const curriedBadMap = curry(badmap) const curriedGoodMap = curry(goodMap) const goodDoubleArray = goodMap(x => x * 2) const badDoubleArray = badMap(_, x => x * 2) #+END_SRC The bad version requires the curry function to support a magic placeholder argument and doesn't look as clean. * Practical Currying Currying is not automatic in JavaScript, as in other languages External tools don't (currently) to statically analyse curried functions Solution: Don't expose curried functions Instead, write functions as if currying were automatic * Functional composition Creating functions from other functions Usually provided by =compose= (right-to-left) and =pipe= (left-to-right) A very simple definition of =compose= for only two functions would look like this #+BEGIN_SRC js function compose (f, g) { return function (...args) { return f(g(...args)) } } #+END_SRC * Functional composition (cont.) #+BEGIN_SRC js const plusOne = x => x + 1 const timesTwo = x => x * 2 const plusOneTimesTwo = compose(timesTwo, plusOne) const timesTwoPlusOne = compose(plusOne, timesTwo) nextDoubled(3) // = (3 + 1) * 2 = 8 timesTwoPlusOne(3) // = (3 * 2) + 1 = 7 #+END_SRC * pipe What about =pipe=? =pipe= does the same thing, but runs the functions the other way around =pipe(f, g)= is the same as =compose(g, f)= * Point-free programming With currying and higher-order functions, we (often) don't need to declare function arguments #+BEGIN_SRC js const modulo = a => b => b % a const eq = a => b => a === b const isEven = x => eq(0)(modulo(2)(x)) const isEvenPointFree = compose(eq(0), modulo(2)) #+END_SRC * Further Resources - [[https://drboolean.gitbooks.io/mostly-adequate-guide/content/][Mostly adequate guide to FP (in javascript)]] - [[http://ramdajs.com/][Ramda]], a general-purpose FP library - [[https://sanctuary.js.org/][Sanctuary]], a JavaScript library for Haskellers