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 Slide

  2. me, hi!

    View Slide

  3. stuff I do

    View Slide

  4. stuff I do

    View Slide

  5. Does it scale?

    View Slide

  6. View Slide

  7. Scales?

    View Slide

  8. Scaling issue #1
    Fog of war

    View Slide

  9. Scaling issue #2
    Complexity

    View Slide

  10. Complex vs Complicated

    View Slide

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

    View Slide

  12. Complicated

    View Slide

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

    View Slide

  14. Complex

    View Slide

  15. Complex (even with perfect visibility)

    View Slide

  16. 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 Slide

  17. Complex system, an example

    View Slide

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

    View Slide

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

    View Slide

  20. Addressing scalability
    issues

    View Slide

  21. Issue 1: Fog of war

    View Slide

  22. Bring in the typecheckers!

    View Slide

  23. View Slide

  24. View Slide

  25. 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 Slide

  26. View Slide

  27. Issue 2: Complexity

    View Slide

  28. Bring in FP!

    View Slide

  29. 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 Slide

  30. Functional
    Programming, for me
    Everything is an expression

    View Slide

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

    View Slide

  32. In other terms...

    View Slide

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

    View Slide

  34. View Slide

  35. Back to Earth...

    View Slide

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

    View Slide

  37. View Slide

  38. Add a typechecker

    View Slide

  39. 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 Slide

  40. How?

    View Slide

  41. 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 Slide

  42. 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 Slide

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

    View Slide

  44. 1. Stop using null and
    undefined

    View Slide

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

    View Slide

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

    View Slide

  47. 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 Slide

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

    View Slide

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

  50. 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 Slide

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

    View Slide

  52. 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 Slide

  53. 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 Slide

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

    View Slide

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

    View Slide

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

    View Slide

  57. 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 Slide

  58. 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 Slide

  59. 2. Stop throwing
    exceptions

    View Slide

  60. 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 Slide

  61. 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 Slide

  62. 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 Slide

  63. 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 Slide

  64. 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 Slide

  65. 3. Stop using Promise

    View Slide

  66. 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 Slide

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

    View Slide

  68. 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 Slide

  69. What about (typed)
    errors?

    View Slide

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

    View Slide

  71. 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 Slide

  72. 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 Slide

  73. 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 Slide

  74. Thank you!

    View Slide

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

    View Slide