Anjana Sofia Vakil
June 17, 2022
150

# 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!

June 17, 2022

## Transcript

width/height

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

element.getAttribute()

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

State • Randomness

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));

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. ### 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

31. ### 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 })

33. ### 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 }))

39. ### const SimplexNoise = require("[email protected]") 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
40. ### 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 })
41. ### 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 })
42. ### look at all we made! • Side effects • Repetition

• “State” • “Randomness”