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.

C2bb0454c4af1a61e7f173d54ce17b0b?s=128

Gabriele Petronella

March 23, 2019
Tweet

Transcript

  1. Gabriele Petronella Functional Programming in front-end applications Codemotion Rome .

    23/03/2019
  2. me, hi!

  3. stuff I do

  4. stuff I do

  5. Does it scale?

  6. None
  7. Scales?

  8. Scaling issue #1 Fog of war

  9. Scaling issue #2 Complexity

  10. Complex vs Complicated

  11. Complicated 4 not simple 4 stll knowable 4 usually composed

    by many pieces 4 can be taken apart and studied
  12. Complicated

  13. Complex 4 not simple 4 dense interdependencies 4 not fully

    knowable 4 hard to predict accurately 4 cannot be taken apart and studied
  14. Complex

  15. Complex (even with perfect visibility)

  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
  17. Complex system, an example

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

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

    !important; }
  20. Addressing scalability issues

  21. Issue 1: Fog of war

  22. Bring in the typecheckers!

  23. None
  24. None
  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.
  26. None
  27. Issue 2: Complexity

  28. Bring in FP!

  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
  30. Functional Programming, for me Everything is an expression

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

  32. In other terms...

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

  34. None
  35. Back to Earth...

  36. So, should I rewrite everything in ____? (Insert language that

    compiles to JS)
  37. None
  38. Add a typechecker

  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
  40. How?

  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
  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
  43. What about FP? Where do I start from?

  44. 1. Stop using null and undefined

  45. 1. Stop using null and undefined interface Device { getActiveCamera():

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

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

    in TypeScript https://github.com/gcanti/fp-ts
  49. 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.
  50. 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)
  51. How to work with Option map head([1, 2]).map(n => n

    + 100); // some(101)
  52. 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)
  53. 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
  54. getOrElse head([42, 43]).getOrElse(-1); // 42 head([]).getOrElse(-1); // -1

  55. Let's try interface Device { getActiveCamera(): Option<Camera>; } interface Camera

    { getResolution(): Option<Resolution>; } interface Resolution { width: number; height: number; }
  56. Using Option device .getActiveCamera() .chain(camera => camera.getResolution()) .map(resolution => `Width

    is ${resolution.width}`) .getOrElse("No width :(");
  57. 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
  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
  59. 2. Stop throwing exceptions

  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
  61. 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>
  62. 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;
  63. 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);
  64. 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")
  65. 3. Stop using Promise

  66. 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;
  67. Welcome Task Task<A> is like Promise<A> but it's lazy and

    referentially transparent.
  68. 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>>
  69. What about (typed) errors?

  70. Welcome TaskEither TaskEither mixes the capabilities of Task and Either

    You could use Task<Either<L, A>> but TaskEither has a more convenient API.
  71. 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"); }); }
  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
  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
  74. Thank you!

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