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

Functional Programming in Front-end Applications

Functional Programming in Front-end Applications

Gabriele Petronella

March 12, 2019
Tweet

More Decks by Gabriele Petronella

Other Decks in Programming

Transcript

  1. Programming with expressions Statement: let permissions = []; if (user.age

    >= 18) { permissions = [generalPermissions]; } else { permissions = [underagePermissions]; }
  2. Programming with expressions Expression: val permissions = if (user.age >=

    18) List(generalPermission) else List(underagePermissions)
  3. Programming with expressions Composing/decomposing const isSubscribed => (user: User) =>

    user.isSubscribedToNewsletter const subscribedUsers = users.filter(isSubscribed) const toNewsletterContent = user => `Hi ${user.firstName}, this is our newsletter` const newsletterContents = subscribedUsers.map(toNewsletterContent) return newsletterContents
  4. Programming with expressions Requirements 4 You can assign subexpressions to

    symbols, and replace them around 4 Function expressions don't "do" anything, only compute values 4 Function expressions use defined inputs and return the defined outputs
  5. Referential transparency 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;
  6. Complicated 4 not simple 4 stll knowable 4 usually composed

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

    knowable 4 hard to predict accurately 4 cannot be taken apart and studied
  8. 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
  9. 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.
  10. 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
  11. 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
  12. Caveat If you're using non-standard JavaScript features, it's likely you

    will have to remove them. TypeScript implement TC39 proposals from Stage 3 on. For example, this won't be parsed: Promise.resolve(42).then(::console.log); // ^^ // bind operator
  13. 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
  14. 1. Stop using null and undefined interface Device { getActiveCamera():

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

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

    in TypeScript https://github.com/gcanti/fp-ts
  18. 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.
  19. 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)
  20. 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)
  21. 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
  22. Let's try interface Device { getActiveCamera(): Option<Camera>; } interface Camera

    { getResolution(): Option<Resolution>; } interface Resolution { width: number; height: number; }
  23. 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
  24. 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
  25. 2. Stop throwing exceptions function validateUser(user: User): User { if

    (user.age >= 18) { return user; } else { throw "User is underage!"; } } validateUser(underageUser).name; // boom
  26. 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>
  27. 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;
  28. 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);
  29. 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")
  30. 3. Stop using Promise Remember this? function question(message: string): Promise<string>

    { // ... } const p = question("What is your name?"); const answer1 = await p; const answer2 = await p;
  31. 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>>
  32. Welcome TaskEither TaskEither mixes the capabilities of Task and Either

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