{"id":19154053,"url":"https://github.com/thinkswan/js-functional-programming-examples","last_synced_at":"2026-06-15T09:30:23.703Z","repository":{"id":139998903,"uuid":"194144824","full_name":"thinkswan/js-functional-programming-examples","owner":"thinkswan","description":"Notes from Jeremy Fairbank's \"Functional Programming Basics in ES6\" talk.","archived":false,"fork":false,"pushed_at":"2019-06-27T18:31:03.000Z","size":5,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-01-03T19:24:06.465Z","etag":null,"topics":["functional-programming","javascript"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thinkswan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-27T18:30:53.000Z","updated_at":"2022-05-17T16:18:04.000Z","dependencies_parsed_at":null,"dependency_job_id":"bf133ef0-9891-4f84-a607-d70a91df4e84","html_url":"https://github.com/thinkswan/js-functional-programming-examples","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thinkswan%2Fjs-functional-programming-examples","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thinkswan%2Fjs-functional-programming-examples/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thinkswan%2Fjs-functional-programming-examples/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thinkswan%2Fjs-functional-programming-examples/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thinkswan","download_url":"https://codeload.github.com/thinkswan/js-functional-programming-examples/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240237880,"owners_count":19769827,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-programming","javascript"],"created_at":"2024-11-09T08:25:17.838Z","updated_at":"2026-06-15T09:30:22.344Z","avatar_url":"https://github.com/thinkswan.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# Functional programming basics in ES6\n\nThese notes are taken from Jeremy Fairbank's talk, [\"Functional Programming Basics in ES6\"](https://www.youtube.com/watch?v=FYXpOjwYzcs).\n\n## What is functional programming?\n\nIt's a paradigm that uses pure functions to build up higher-order functions.\n\nA function simply maps an input (domain) to an output (range).\n\n## Why use functional programming?\n\n- **Predictable:** Pure, declarative functions\n- **Safe:** State is immutable\n- **Transparent:** State is first-class\n- **Modular:** Compose first-class functions\n\n## Functional programming libs\n\n- React\n- Redux\n- Lodash\n- Ramda\n\n## ES6 features useful in functional programming\n\n### Arrow functions\n\n```javascript\nconst add = (x, y) =\u003e x + y // add(2, 3) === 5\nconst identity = x =\u003e x // identity(1) === 1\n```\n\n### Rest-spread operator\n\n```javascript\nconst array = (...elements) =\u003e elements // array(1, 2, 3) == [1, 2, 3]\nconst log = (...args) =\u003e console.log(...args) // log('Hello', 'Poznań') == 'Hello Poznań'\n```\n\n### Destructuring\n\n```javascript\nconst [js, ...rest] = [\"JavaScript\", \"Ruby\", \"Haskell\"] // js === 'JavaScript', rest == ['Ruby', 'Haskell']\nconst head = ([x]) =\u003e x // head([1, 2, 3]) === 1\n```\n\n### Default arguments\n\n```javascript\nconst greet = (name, greeting = \"Hello\") =\u003e console.log(greeting, name) // greet('Poznań') == 'Hello Poznań'\n```\n\n### Object merging\n\n```javascript\nObject.assign({}, { hello: \"Poznań\" }, { hi: \"Warsaw\" }) // { hello: 'Poznań', hi: 'Warsaw' }\n```\n\n### ES6 classes\n\n```javascript\nclass Point {\n  // Constructors desugar to functions, eg. function Point(x, y) {}\n  constructor(x, y) {\n    this.x = x\n    this.y = y\n  }\n\n  // Instance methods desugar to prototype methods, eg. Point.prototype.moveBy = function(dx, dy) {}\n  moveBy(dx, dy) {\n    this.x == dx\n    this.y == dy\n  }\n}\n```\n\n## Predictable: Pure, declarative functions\n\nPure functions respect the following criteria:\n\n- No side effects (including mutations and printing)\n- No dependencies (including global state)\n- Idempotent (inputs always map to the same outputs, regardless of how many times the function is called)\n\nTo illustrate the benefits of pure functions, consider these impure functions:\n\n```javascript\nlet name = \"Alice\"\n\n// Depends on global variable\nconst getName = () =\u003e name\n\n// Mutates state\nconst setName = newName =\u003e (name = newName)\n\n// Depends on global variable _and_ mutates state\nconst printUpperName = () =\u003e console.log(name.toUpperCase())\n```\n\nThese functions are difficult to test:\n\n```javascript\ndescribe(\"api\", () =\u003e {\n  beforeEach(() =\u003e mockConsoleLog())\n  afterEach(() =\u003e restoreConsoleLog())\n\n  it(\"sets and prints the name\", () =\u003e {\n    printUpperName()\n\n    expect(console.log).calledWith(\"ALICE\")\n\n    setName(\"Bob\")\n    printUpperName()\n\n    expect(console.log).calledWith(\"BOB\")\n  })\n})\n```\n\nHow can we rewrite this example as a pure function with tests?\n\n```javascript\nconst upperName = name =\u003e name.toUpperCase()\n\ndescribe(\"api\", () =\u003e {\n  it(\"returns an uppercase name\", () =\u003e {\n    expect(upperName(\"Alice\").to.equal(\"ALICE\"))\n    expect(upperName(\"Bob\").to.equal(\"BOB\"))\n  })\n})\n```\n\nAn **imperative** function describes _how to achieve the result_. Consider this function:\n\n```javascript\nconst doubleNumbers = numbers =\u003e {\n  const doubled = []\n\n  for (let i = 0; i \u003c numbers.length; i++) {\n    doubled.push(numbers[i] * 2)\n  }\n\n  return doubled\n}\n\ndoubleNumbers([1, 2, 3]) // [2, 4, 6]\n```\n\nA **declarative** function declares _what the desired result is_. To rewrite the above function declaratively:\n\n```javascript\nconst doubleNumbers = numbers =\u003e numbers.map(n =\u003e n * 2)\n\ndoubleNumbers([1, 2, 3]) // [2, 4, 6]\n```\n\n## Safe: State is immutable\n\nState should be created, not mutated.\n\nTo illustrate the benefits of immutable state, consider this example:\n\n```javascript\nconst hobbies = [\"programming\", \"reading\", \"music\"]\n\nconst firstTwo = hobbies.splice(0, 2) // ['programming', 'reading']\n\nconsole.log(hobbies) // ['music']\n```\n\nOne way to enforce immutable state is to use `Object#freeze`:\n\n```javascript\nconst hobbies = Object.freeze[(\"programming\", \"reading\", \"music\")]\n\nconst firstTwo = hobbies.splice(0, 2) // TypeError\n```\n\nAnother approach is to free the state. Consider the `Point` class from earlier:\n\n```javascript\nclass Point {\n  constructor(x, y) {\n    this.x = x\n    this.y = y\n  }\n\n  moveBy(dx, dy) {\n    this.x == dx\n    this.y == dy\n  }\n}\n\nconst point = new Point(0, 0)\n\npoint.moveBy(5, 5)\npoint.moveBy(-2, 2)\n\nconsole.log([point.x, point.y]) // [3, 7]\n```\n\nWe can free the state in this class as follows:\n\n```javascript\nconst createPoint = (x, y) =\u003e Object.freeze([x, y])\n\nconst movePointBy = ([x, y], dx, dy) =\u003e Object.freeze([x + dx, y + dy])\n\nlet point = createPoint(0, 0)\n\npoint = movePointBy(point, 5, 5)\npoint = movePointBy(point, -2, 2)\n\nconsole.log(point) // [3, 7]\n```\n\nSince immutable state requires us to return new data structures with each call, it has some pros and cons:\n\n- Pros\n  - Safety\n  - Free undo/redo logs (eg. Redux)\n  - Explicit flow of data\n  - Concurrency safety\n- Cons\n  - Verbose\n  - More object creation\n  - More garbage collections\n  - More memory usage\n\n## Modular: Compose first-class functions\n\nJavaScript treats functions as first-class citizens. This means you can assign them to variables, pass them as input, and receive them as ouput, just like you can a boolean, number, or string.\n\nBefore continuing, we should define a few terms:\n\n- **Higher-order functions** return a new function.\n- **Closures** encapsulate state.\n- **Partially-applied functions** return a new function with 1 or more of the inputs set (similar to `bind`).\n- **Curryable functions** are functions that can be partially-applied and will invoke once all inputs are set.\n\nConsider the following example:\n\n```javascript\n// This function is both a higher-order function (because it returns a new function) and a closure (because it \"closes over\" `x`)\nconst createAdder = x =\u003e y =\u003e x + y\n\n// This function is a partially-applied function (because it applies `x = 3`, but not `y`)\nconst add3 = createAdder(3)\n\nadd3(2) // 5\nadd3(3) // 6\n```\n\nPerhaps a more practical example:\n\n```javascript\nconst request = options =\u003e {\n  return fetch(options.url, options).then(resp =\u003e resp.json())\n}\n\nconst usersPromise = request({\n  url: \"/users\",\n  headers: { \"X-Custom\": \"myKey\" }\n})\nconst tasksPromise = request({\n  url: \"/tasks\",\n  headers: { \"X-Custom\": \"myKey\" }\n})\n```\n\nWe can make this more reusable as follows:\n\n```javascript\nconst createRequester = options =\u003e {\n  return otherOptions =\u003e request(Object.assign({}, options, otherOptions))\n}\n\nconst customRequest = createRequester({ headers: { \"X-Custom\": \"myKey\" } })\n\nconst usersPromise = customRequest({ url: \"/users\" })\nconst tasksPromise = customRequest({ url: \"/tasks\" })\n```\n\nMoving on to curryable functions, let's recreate the adder and requester from above:\n\n```javascript\nconst add = x =\u003e y =\u003e x + y\nconst request = defaults =\u003e options =\u003e {\n  options = Object.assign({}, defaults, options)\n\n  return fetch(options.url, options).then(resp =\u003e resp.json())\n}\n```\n\nWith the building blocks of higher-order functions, closures, partially-applied functions, and curryable functions, let's look at a shopping cart example:\n\n```javascript\nconst map = fn =\u003e array =\u003e array.map(fn)\nconst multiply = x =\u003e y =\u003e x * y\nconst pluck = key =\u003e object =\u003e object[key]\n\nconst discount = multiply(0.98)\nconst tax = multiply(1.0925)\nconst customRequest = request({ headers: { \"X-Custom\": \"myKey\" } })\n\ncustomRequest({ url: \"/cart/items\" }) // [{ price: 5 }, { price: 10 }, { price: 3 }]\n  .then(map(pluck(\"price\"))) // [5, 10, 3]\n  .then(map(discount)) // [4.9, 9.8, 2.94]\n  .then(map(tax)) // [5.35, 10.71, 3.21]\n```\n\nWe can also compose closures:\n\n```javascript\nconst processWord = compose(\n  hyphenate,\n  reverse,\n  toUpperCase\n) // Same as `word =\u003e hyphenate(reverse(toUpperCase(word)))`\n\nconst words = [\"hello\", \"functional\", \"programming\"]\n\nconst newWords = words.map(processWord) // ['OL-LEH', 'LANOI-TCNUF', 'GNIMM-ARGORP']\n```\n\nTo improve the performance of the shopping cart example, we can replace the 3 `map` interations with a single iteration:\n\n```javascript\ncustomRequest({ url: \"/cart/items\" }) // [{ price: 5 }, { price: 10 }, { price: 3 }]\n  .then(\n    map(\n      compose(\n        tax,\n        discount,\n        pluck(\"price\")\n      )\n    )\n  ) // [5, 10, 3] =\u003e [4.9, 9.8, 2.94] =\u003e [5.35, 10.71, 3.21]\n```\n\nFinally, to handle loops in functional programming, we can use recursion. Consider a function that solves a factorial:\n\n```javascript\nconst factorial = n =\u003e {\n  let result = 1\n\n  while (n \u003e 1) {\n    result *= n\n    n--\n  }\n\n  return result\n}\n```\n\nTo rewrite this recursively:\n\n```javascript\nconst factorial = n =\u003e {\n  if (n \u003c 2) return 1\n\n  return n * factorial(n - 1)\n}\n```\n\nTo avoid exceeding the call stack size, we can optimize this further using tail call optimization:\n\n```javascript\nconst factorial = (n, accum = 1) =\u003e {\n  if (n \u003c 2) return accum\n\n  return factorial(n - 1, n * accum)\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthinkswan%2Fjs-functional-programming-examples","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthinkswan%2Fjs-functional-programming-examples","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthinkswan%2Fjs-functional-programming-examples/lists"}