$30 off During Our Annual Pro Sale. View Details »

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!

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

    View Slide

  2. functional programming is
    beautiful!

    View Slide

  3. functional programming is
    easy to predict

    View Slide

  4. functional programming is
    easy to test

    View Slide

  5. functional programming is
    easy to debug

    View Slide

  6. functional programming is
    easy to love

    View Slide

  7. functional programming is
    easy to fear?

    View Slide

  8. functional programming is
    easy (enough) to
    understand

    View Slide

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

    View Slide

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

    View Slide

  11. functional programming is
    programming with
    functions

    View Slide

  12. functional programming is
    programming with
    pure functions

    View Slide

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

    View Slide

  14. a pure function is
    deterministic, stateless
    same input => same output

    View Slide

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

    View Slide

  16. functional programming is
    pretty useless?

    View Slide

  17. how does FP work in the
    real world?

    View Slide

  18. let’s make
    functional
    art!

    View Slide

  19. generative art
    usually needs
    ● Side effects
    ● Repetition
    ● State
    ● Randomness

    View Slide

  20. how does FP handle
    side effects?

    View Slide

  21. imperative shell
    pure
    functional
    core
    deterministic in here
    safe & sound
    side effects out here

    View Slide

  22. viewBox="0 0 100 100"
    xmlns="http://www.w3.org/2000/svg">
    function impureSetSVGContents(contents) {
    const art = document.getElementById("art");
    art.innerHTML = contents;
    return art;
    }
    impureSetSVGContents(pureGetArt(params));

    View Slide

  23. let’s make
    patterns!

    View Slide

  24. how does FP handle
    repetition?

    View Slide

  25. imperative:
    iteration
    functional:
    recursion

    View Slide

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

    View Slide

  27. function getPattern(color1, color2, width) {
    const thisTile =
    ``;
    if (width <= 20) { return `${thisTile}`; }
    return `${thisTile}

    ${getPattern(color2, color1, width * hyp)}
    `;
    }
    getPattern("limegreen", "rebeccapurple", 100)
    base case
    recursive case

    View Slide

  28. View Slide

  29. View Slide

  30. View Slide

  31. View Slide

  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
    `
    ${getTiles(width-colWidth, rowHeight, cols-1, 1, makeTile, newData)}
    `
    : ``}
    ${rows > 1 ? // rows below this one
    `
    ${getTiles(width, height-rowHeight, cols, rows-1, makeTile, newData)}
    `
    : ``}`;}
    function makeBasicTile(width, height, { color }) { return [
    ``,
    { color } ];}
    getTiles(100, 100, 5, 5, basicTile, { color: "orange" })
    base
    recursive
    recursive

    View Slide

  33. how does FP handle
    state?

    View Slide

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

    View Slide

  35. function makeCheckerTile(width, height, { colors, colorIndex }) {
    return [
    ``,
    { colors, colorIndex: (colorIndex + 1) % colors.length }
    ];
    }
    getTiles(100, 100, 5, 5, makeCheckerTile, {
    colors: ["orange", "hotPink"],
    colorIndex: 0
    })

    View Slide

  36. let’s make
    functional
    fashion!
    design: palmettadesign.hu

    View Slide

  37. View Slide

  38. View Slide

  39. View Slide

  40. View Slide

  41. View Slide

  42. View Slide

  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}
    ${narrow}
    ${wide}
    ${small}`,
    data
    ];}
    rotate(getTiles(200, 200, 8, 8, tiledTile, {
    colors: ["orange", "hotpink"], colorIndex: 0, n: 5
    }))

    View Slide

  44. let’s get
    irregular!

    View Slide

  45. how does FP handle
    randomness?

    View Slide

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

    View Slide

  47. but we can get
    pseudo-randomness

    View Slide

  48. zoomed out zoomed in
    simplex noise

    View Slide

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

    View Slide

  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 [
    ``,
    { noiseField, colors, tileIndex: tileIndex + 1 }
    ];
    }
    getTiles(100, 100, 30, 30, makeRandomTile, {
    colors: ["orange", "hotpink", "purple"],
    noiseField: myNoise,
    tileIndex: 0
    })

    View Slide

  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 [
    `fill="${colors[colorIndex]}" />
    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
    })

    View Slide

  52. look at all we
    made!
    ● Side effects
    ● Repetition
    ● “State”
    ● “Randomness”

    View Slide

  53. Thanks!
    @AnjanaVakil
    observablehq.com/@anjana
    slide design: slidescarnival.com

    View Slide