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. Complicated 4 not simple 4 stll knowable 4 usually composed

    by many pieces 4 can be taken apart and studied
  2. Complex 4 not simple 4 dense interdependencies 4 not fully

    knowable 4 hard to predict accurately 4 cannot be taken apart and studied
  3. 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
  4. 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.
  5. 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
  6. 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
  7. 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
  8. 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
  9. 1. Stop using null and undefined interface Device { getActiveCamera():

    Camera | undefined; } interface Camera { getResolution(): Resolution | undefined; } interface Resolution { width: number; height: number; }
  10. 1. Stop using null and undefined const width = device.getActiveCamera().getResolution().width;

    if (width != null) { return 'No width :('; } return `Width is ${width}';
  11. 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}';
  12. Welcome fp-ts fp-ts is a library for typed functional programming

    in TypeScript https://github.com/gcanti/fp-ts
  13. Welcome Option Option<A> is a container for an optional value

    of type A. 4 If the value is there, we have an instance of Some<A>. 4 If the value is not there, we have an instance of None.
  14. Example import { Option, some, none } from "fp-ts/lib/Option"; function

    head<A>(arr: Array<A>): Option<A> { return arr.length > 0 ? some(arr[0]) : none; } head([]); // none head([1, 2]); // some(1)
  15. How to work with Option nested map function doSomething(n: number):

    Option<A> { 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)
  16. chain function doSomething(n: number): Option<A> { 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
  17. Let's try interface Device { getActiveCamera(): Option<Camera>; } interface Camera

    { getResolution(): Option<Resolution>; } interface Resolution { width: number; height: number; }
  18. It's not me, it's them! import { fromNullable } from

    'fp-ts/lib/Option' function safeFind<A>(arr: Array<A>, f: A => Boolean): Option<A> { 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
  19. 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
  20. 2. Stop throwing exceptions function validateUser(user: User): User { if

    (user.age >= 18) { return user; } else { throw "User is underage!"; } } validateUser(underageUser).name; // boom
  21. Welcome Either Remember Option<A>? Either<L, R> is a more powerful

    alternative: 4 when the value is there: Right<R> 4 when the value is not there: Left<L>
  22. import { Either, right, left } from "fp-ts/lib/Either"; function validateUser(user:

    User): Either<string, User> { if (user.age >= 18) { return right(user); } else { return left("User is underage!"); } } // compile error: 'name' is not a member of 'Either<string, User>' validateUser(underageUser).name;
  23. Using Either Familiar? declare function doSomething(u: User): Either<string, User>; validateUser(user).map(u

    => u.name); validateUser(user).mapLeft(e => `Error was: ${e}`); validateUser(user).chain(user => doSomething(user)); validateUser(user).getOrElse(defaultUser);
  24. It's not me, it's them! import { tryCatch } from

    "fp-ts/lib/Either"; function safeParseJson(raw: string): Either<string, unknown> { 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")
  25. 3. Stop using Promise declare function question(message: string): Promise<string>; 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;
  26. Example declare function question(message: string): Task<string>; const q = question("What

    is your name?"); const t = q.chain(answer1 => q.map(answer2 => [answer1, answer2]) ); // Task<Array<string>> //////////////// nothing is executing yet //////////////// t.run(); // Promise<Array<string>>
  27. Welcome TaskEither TaskEither mixes the capabilities of Task and Either

    You could use Task<Either<L, A>> but TaskEither has a more convenient API.
  28. Example import { TaskEither, tryCatch, taskEither, fromLeft } from "fp-ts/lib/TaskEither";

    function validate(user: User): TaskEither<string, User> { tryCatch(() => fetch("/myApi/validateUser", { method: "POST", body: JSON.stringify(user) }) ).chain(response => { return reponse.ok ? taskEither.of(user) : fromLeft("User is invalid"); }); }
  29. 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
  30. 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