Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Art of Functional Programming

The Art of Functional Programming

Functional Programming (FP), a paradigm in which programs are made up of pure, stateless functions, is adored by many programmers for how easy it makes it to predict, test, and debug the behavior of the code we write. Although FP has an unfortunate reputation as an ivory-tower domain full of obscure jargon comprehensible only to those with PhDs in category theory, the core concepts are straightforward ideas all of us can understand, and even have fun learning!

In this talk, we’ll create generative SVG art as a way to explore fundamental FP concepts such as recursion and function composition, and tackle the practical challenges of FP in the real world, such as dealing with the tricky parts like side effects and randomness which don’t fit into the safe, sterile world of pure functions. By the end of the talk we’ll not only have the key concepts & techniques we need to start writing our own functional programs, we’ll have some pretty functional art to look at too!

654527a5cff1756177ef0b1bb0af7aa3?s=128

Anjana Sofia Vakil

June 17, 2022
Tweet

More Decks by Anjana Sofia Vakil

Other Decks in Programming

Transcript

  1. The Art of Functional Programming @AnjanaVakil

  2. functional programming is beautiful!

  3. functional programming is easy to predict

  4. functional programming is easy to test

  5. functional programming is easy to debug

  6. functional programming is easy to love

  7. functional programming is easy to fear?

  8. functional programming is easy (enough) to understand

  9. “Learning Functional Programming with JS” youtu.be/e-5obm1G_FY

  10. frontendmasters.com/courses/functional-first-steps

  11. functional programming is programming with functions

  12. functional programming is programming with pure functions

  13. a pure function is input => output (width, height) =>

    width/height
  14. a pure function is deterministic, stateless same input => same

    output
  15. a pure function is free of side effects console.log() document.append()

    element.getAttribute()
  16. functional programming is pretty useless?

  17. how does FP work in the real world?

  18. let’s make functional art!

  19. generative art usually needs • Side effects • Repetition •

    State • Randomness
  20. how does FP handle side effects?

  21. imperative shell pure functional core deterministic in here safe &

    sound side effects out here
  22. <svg id="art" width=500 height=500 viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"></svg> function

    impureSetSVGContents(contents) { const art = document.getElementById("art"); art.innerHTML = contents; return art; } impureSetSVGContents(pureGetArt(params));
  23. let’s make patterns!

  24. how does FP handle repetition?

  25. imperative: iteration functional: recursion

  26. “Recursion, Iteration, and JavaScript: A Love Story" youtu.be/FmiQr4nfoPQ

  27. function getPattern(color1, color2, width) { const thisTile = `<rect x=0

    y=0 width=${width} height=${width} fill="${color1}" />`; if (width <= 20) { return `${thisTile}`; } return `${thisTile} <g transform="translate(${width / 2}) rotate(45) "> ${getPattern(color2, color1, width * hyp)} </g>`; } getPattern("limegreen", "rebeccapurple", 100) base case recursive case
  28. None
  29. None
  30. None
  31. None
  32. function getTiles(width, height, cols, rows, makeTile, data) { const [colWidth,

    rowHeight] = [width / cols, height / rows]; const [thisTile, newData] = makeTile(colWidth, rowHeight, data); return `${thisTile} ${cols > 1 ? // rest of this row to the right of this tile `<g transform="translate(${colWidth}, 0)"> ${getTiles(width-colWidth, rowHeight, cols-1, 1, makeTile, newData)} </g>` : ``} ${rows > 1 ? // rows below this one `<g transform="translate(0, ${rowHeight})"> ${getTiles(width, height-rowHeight, cols, rows-1, makeTile, newData)} </g>` : ``}`;} function makeBasicTile(width, height, { color }) { return [ `<rect width=${width} height=${height} stroke="black" fill="${color}" />`, { color } ];} getTiles(100, 100, 5, 5, basicTile, { color: "orange" }) base recursive recursive
  33. how does FP handle state?

  34. treat state like data [input, oldState] => [output, newState]

  35. function makeCheckerTile(width, height, { colors, colorIndex }) { return [

    `<rect x=0 y=0 width=${width} height=${height} fill="${colors[colorIndex]}" />`, { colors, colorIndex: (colorIndex + 1) % colors.length } ]; } getTiles(100, 100, 5, 5, makeCheckerTile, { colors: ["orange", "hotPink"], colorIndex: 0 })
  36. let’s make functional fashion! design: palmettadesign.hu

  37. None
  38. None
  39. None
  40. None
  41. None
  42. None
  43. function makeTiledTile(width, height, data) { const split = 0.7; const

    [sWidth, sHeight] = [width*split, height*split]; const newData = {...data, colorIndex: data.n % 2 === 0 ? data.colorIndex : (data.colorIndex + 1) % data.colors.length }; const big = getTiles(sWidth, sHeight, …, makeCheckerTile, data); const narrow = getTiles(width-sWidth, sHeight, …, makeCheckerTile, newData); const wide = getTiles(sWidth, height-sHeight, …, makeCheckerTile, newData); const small = getTiles(width-sWidth, height-sHeight, …, makeCheckerTile, data); return [ `${big} <g transform="translate(${sWidth}, 0)">${narrow}</g> <g transform="translate(0, ${sHeight})">${wide}</g> <g transform="translate(${sWidth}, ${sHeight})">${small}</g>`, data ];} rotate(getTiles(200, 200, 8, 8, tiledTile, { colors: ["orange", "hotpink"], colorIndex: 0, n: 5 }))
  44. let’s get irregular!

  45. how does FP handle randomness?

  46. it doesn’t! (nor does any computation)

  47. but we can get pseudo-randomness

  48. zoomed out zoomed in simplex noise

  49. const SimplexNoise = require("simplex-noise@2.4") function impureGetNoiseField(seed) { return new SimplexNoise(seed);

    } const myNoise = impureGetNoiseField("hello noise!"); myNoise.noise2D(50, 50); // 0.38978764467539995 const otherNoise = impureGetNoiseField("other noise!"); otherNoise.noise2D(50,50); // -0.10783404678637644 const sameNoise = impureGetNoiseField("hello noise!"); sameNoise.noise2D(50,50); // 0.38978764467539995
  50. function makeRandomTile(width, height, { colors, noiseField, tileIndex }) { const

    noiseHere = noiseField.noise2D(tileIndex * width, tileIndex * height); const colorIndex = Math.floor(Math.abs(noiseHere * 50)) % colors.length; return [ `<rect x=0 y=0 width=${width} height=${height} fill="${colors[colorIndex]}" />`, { noiseField, colors, tileIndex: tileIndex + 1 } ]; } getTiles(100, 100, 30, 30, makeRandomTile, { colors: ["orange", "hotpink", "purple"], noiseField: myNoise, tileIndex: 0 })
  51. function makeClockTile(width, height, data) { const { colors, tileIndex, noiseField

    } = data; const noiseHere = noiseField.noise2D(tileIndex * width, tileIndex * height); const degree = 45 * (noiseHere * 4); const colorIndex = tileIndex % colors.length; return [ `<circle cx=${width/2} cy=${height/2} r=${width/2-1} stroke="black" fill="${colors[colorIndex]}" /> <line x1=${width/2} x2=${width/2} y1=1 y2=${height/2-1} stroke="black" transform="rotate(${degree}, ${width/2}, ${height/2})"/>`, { ...data, tileIndex: tileIndex + 1} ]; } getTiles(100, 100, 5, 5, clockTile, { noiseField: myNoise, colors: ["yellow", "limegreen", "hotpink", "orange"], tileIndex: 0 })
  52. look at all we made! • Side effects • Repetition

    • “State” • “Randomness”
  53. Thanks! @AnjanaVakil observablehq.com/@anjana slide design: slidescarnival.com