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

Functional Programming in front-end applications

Functional Programming in front-end applications

Functional programming is becoming pervasive in every branch of development, but why should you care? Can we reap the benefits of FP, if our job is to write web UIs?

In this presentation, we'll see how FP fits into the front-end development picture and how we can leverage TypeScript to progressively migrate our codebases and start getting the most out of it.

Gabriele Petronella

March 23, 2019
Tweet

More Decks by Gabriele Petronella

Other Decks in Programming

Transcript

  1. Gabriele Petronella
    Functional Programming in
    front-end applications
    Codemotion Rome . 23/03/2019

    View full-size slide

  2. Does it scale?

    View full-size slide

  3. Scaling issue #1
    Fog of war

    View full-size slide

  4. Scaling issue #2
    Complexity

    View full-size slide

  5. Complex vs Complicated

    View full-size slide

  6. Complicated
    4 not simple
    4 stll knowable
    4 usually composed by many pieces
    4 can be taken apart and studied

    View full-size slide

  7. Complex
    4 not simple
    4 dense interdependencies
    4 not fully knowable
    4 hard to predict accurately
    4 cannot be taken apart and studied

    View full-size slide

  8. Complex (even with perfect visibility)

    View full-size slide

  9. Complex
    Complex problems are tricker because:
    4 their components behave differently depending on
    the global system behavior
    4 adding/removing components affects the global
    system behavior

    View full-size slide

  10. Complex system, an example

    View full-size slide

  11. Complex system,
    another example
    registration.css
    p {
    font-family: "Arial";
    }

    View full-size slide

  12. Complex system,
    another example
    main.css
    p {
    font-family: "Reenie Beanie" !important;
    }

    View full-size slide

  13. Addressing scalability
    issues

    View full-size slide

  14. Issue 1: Fog of war

    View full-size slide

  15. Bring in the typecheckers!

    View full-size slide

  16. Typechecker = Visibility
    A typechecker will give you visibility over the impacts
    of a change,
    allowing you to iterate faster and with more
    confidence.
    It does not replace tests, it complements them.

    View full-size slide

  17. Issue 2: Complexity

    View full-size slide

  18. Bring in FP!

    View full-size slide

  19. Functional Programming
    In computer science, functional programming is a
    programming paradigm [...]
    that treats computation as the evaluation of
    mathematical functions and avoids
    changing-state and mutable data.
    -- someone on Wikipedia

    View full-size slide

  20. Functional
    Programming, for me
    Everything is an expression

    View full-size slide

  21. Functional Programming is boring
    Functional Programming patterns - Scott Wlaschin

    View full-size slide

  22. In other terms...

    View full-size slide

  23. Functional Programming
    makes problems
    complicated
    (as opposed to complex)

    View full-size slide

  24. Back to Earth...

    View full-size slide

  25. So, should I rewrite
    everything in ____?
    (Insert language that compiles to JS)

    View full-size slide

  26. Add a typechecker

    View full-size slide

  27. TypeScript
    4 Superset of Javascript by Microsoft
    4 "pragmatic" type system with local inference
    4 "progressive" type system (can have untyped parts)
    4 community oriented

    View full-size slide

  28. TS migration vademecum
    4 make your JS code work with a TS toolchain
    4 write new features in TS
    4 write definition files for your internal ecosystem
    4 migrate existing JS sources to TS

    View full-size slide

  29. Case study
    4 50k+ LOC JS project
    4 entirely ported to TS in 3 months, while actively
    developing features
    4 internal ecosystem shared between JS and TS
    4 initial port of ecosystem using definition files, then
    gradually rewritten

    View full-size slide

  30. What about FP?
    Where do I start from?

    View full-size slide

  31. 1. Stop using null and
    undefined

    View full-size slide

  32. 1. Stop using null and undefined
    interface Device {
    getActiveCamera(): Camera | undefined;
    }
    interface Camera {
    getResolution(): Resolution | undefined;
    }
    interface Resolution {
    width: number;
    height: number;
    }

    View full-size slide

  33. 1. Stop using null and undefined
    const width = device.getActiveCamera().getResolution().width;
    if (width != null) {
    return 'No width :(';
    }
    return `Width is ${width}';

    View full-size slide

  34. 1. Stop using null and undefined
    let width = undefined;
    const camera = device.getActiveCamera();
    if (camera != null) {
    const resolution = camera.getResolution();
    if (resolution != null) {
    width = resolution.width;
    }
    }
    if (width != null) {
    return 'No width :(';
    }
    return `Width is ${width}';

    View full-size slide

  35. Welcome fp-ts
    fp-ts is a library for typed
    functional programming in
    TypeScript
    https://github.com/gcanti/fp-ts

    View full-size slide

  36. Welcome Option
    Option is a container for an optional value of type A.
    4 If the value is there, we have an instance of Some.
    4 If the value is not there, we have an instance of None.

    View full-size slide

  37. Example
    import { Option, some, none } from "fp-ts/lib/Option";
    function head(arr: Array): Option {
    return arr.length > 0 ? some(arr[0]) : none;
    }
    head([]); // none
    head([1, 2]); // some(1)

    View full-size slide

  38. How to work with Option
    map
    head([1, 2]).map(n => n + 100); // some(101)

    View full-size slide

  39. How to work with Option
    nested map
    function doSomething(n: number): Option {
    return number > 1 ? some(n.toString()) : none;
    }
    head([2, 3]).map(n => doSomething(n)); // some(some("2"))
    head([1, 2]).map(n => doSomething(n)); // some(none)

    View full-size slide

  40. chain
    function doSomething(n: number): Option {
    return number > 1 ? some(n.toString()) : none;
    }
    head([2, 3]).chain(n => doSomething(n)); // some("2")
    head([1, 2]).chain(n => doSomething(n)); // none
    head([]).chain(n => doSomething(n)); // none

    View full-size slide

  41. getOrElse
    head([42, 43]).getOrElse(-1); // 42
    head([]).getOrElse(-1); // -1

    View full-size slide

  42. Let's try
    interface Device {
    getActiveCamera(): Option;
    }
    interface Camera {
    getResolution(): Option;
    }
    interface Resolution {
    width: number;
    height: number;
    }

    View full-size slide

  43. Using Option
    device
    .getActiveCamera()
    .chain(camera => camera.getResolution())
    .map(resolution => `Width is ${resolution.width}`)
    .getOrElse("No width :(");

    View full-size slide

  44. It's not me, it's them!
    import { fromNullable } from 'fp-ts/lib/Option'
    function safeFind(arr: Array, f: A => Boolean): Option {
    return fromNullable(arr.find(f))
    }
    [1, 2, 3].find(x => x < 3) // 2
    [1, 2, 3].find(x => x < 0) // undefined
    safeFind([1, 2, 3], x => x < 3) // some(2)
    safeFind([1, 2, 3], x => x < 0) // none

    View full-size slide

  45. In real life
    import { array } from "fp-ts/lib/Array";
    array.find([1, 2, 3], x => x < 3); // some(2)
    array.find([1, 2, 3], x => x < 0); // none

    View full-size slide

  46. 2. Stop throwing
    exceptions

    View full-size slide

  47. 2. Stop throwing exceptions
    function validateUser(user: User): User {
    if (user.age >= 18) {
    return user;
    } else {
    throw "User is underage!";
    }
    }
    validateUser(underageUser).name; // boom

    View full-size slide

  48. Welcome Either
    Remember Option? Either is a more powerful
    alternative:
    4 when the value is there: Right
    4 when the value is not there: Left

    View full-size slide

  49. import { Either, right, left } from "fp-ts/lib/Either";
    function validateUser(user: User): Either {
    if (user.age >= 18) {
    return right(user);
    } else {
    return left("User is underage!");
    }
    }
    // compile error: 'name' is not a member of 'Either'
    validateUser(underageUser).name;

    View full-size slide

  50. Using Either
    Familiar?
    declare function doSomething(u: User): Either;
    validateUser(user).map(u => u.name);
    validateUser(user).mapLeft(e => `Error was: ${e}`);
    validateUser(user).chain(user => doSomething(user));
    validateUser(user).getOrElse(defaultUser);

    View full-size slide

  51. It's not me, it's them!
    import { tryCatch } from "fp-ts/lib/Either";
    function safeParseJson(raw: string): Either {
    return tryCatch(() => JSON.parse(raw)).mapLeft(e => e.message);
    }
    JSON.parse("{}"); // {}
    JSON.parse("{"); //
    !
    SyntaxError: Unexpected end of JSON input
    safeParseJson("{}"); // right({})
    safeParseJson("{"); // left("Unexpected end of JSON input")

    View full-size slide

  52. 3. Stop using Promise

    View full-size slide

  53. 3. Stop using Promise
    declare function question(message: string): Promise;
    const answer1 = await question("What is your name?");
    const answer2 = await question("What is your name?");
    vs
    const p = question("What is your name?");
    const answer1 = await p;
    const answer2 = await p;

    View full-size slide

  54. Welcome Task
    Task is like Promise but it's lazy and referentially
    transparent.

    View full-size slide

  55. Example
    declare function question(message: string): Task;
    const q = question("What is your name?");
    const t = q.chain(answer1 =>
    q.map(answer2 => [answer1, answer2])
    ); // Task>
    //////////////// nothing is executing yet ////////////////
    t.run(); // Promise>

    View full-size slide

  56. What about (typed)
    errors?

    View full-size slide

  57. Welcome TaskEither
    TaskEither mixes the capabilities of Task and Either
    You could use Task> but TaskEither has a
    more convenient API.

    View full-size slide

  58. Example
    import {
    TaskEither, tryCatch, taskEither, fromLeft
    } from "fp-ts/lib/TaskEither";
    function validate(user: User): TaskEither {
    tryCatch(() => fetch("/myApi/validateUser", {
    method: "POST", body: JSON.stringify(user)
    })
    ).chain(response => {
    return reponse.ok ?
    taskEither.of(user) :
    fromLeft("User is invalid");
    });
    }

    View full-size slide

  59. Control Flow: vanilla vs FP
    vanilla FP
    optionality typeof x != null / && map/chain
    errors
    try catch map/chain
    async (Promise) then / catch map/chain
    async (async/await)
    try catch map/chain
    ...
    whatever else something ad-hoc map/chain

    View full-size slide

  60. Resources
    4 https://gcanti.github.io/fp-ts/
    4 https://github.com/MostlyAdequate/mostly-
    adequate-guide
    4 Functional Programming Patterns
    4 https://www.youtube.com/watch?v=E8I19uA-wGY
    4 http://italiajs.herokuapp.com/
    4 channels #fp and #typescript

    View full-size slide

  61. questions.d.ts
    @gabro27
    https://buildo.io/careers

    View full-size slide