Slide 1

Slide 1 text

Gabriele Petronella Functional Programming in front-end applications FpInBo. 12/03/2019

Slide 2

Slide 2 text

me, hi!

Slide 3

Slide 3 text

stuff I do

Slide 4

Slide 4 text

stuff I do

Slide 5

Slide 5 text

What is FP?

Slide 6

Slide 6 text

Programming with expressions

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

Programming with expressions Expression: val permissions = if (user.age >= 18) List(generalPermission) else List(underagePermissions)

Slide 9

Slide 9 text

Programming with expressions Composing/decomposing return users .filter(user => user.isSubscribedToNewsletter) .map(user => `Hi ${user.firstName}, this is our newsletter`);

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Referential transparency

Slide 13

Slide 13 text

Referential transparency 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;

Slide 14

Slide 14 text

Ok, but...

Slide 15

Slide 15 text

...is it useful while working on the front-end?

Slide 16

Slide 16 text

Let's talk about "scalability"

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

Scales?

Slide 19

Slide 19 text

Scaling issue #1 Fog of war

Slide 20

Slide 20 text

Scaling issue #2 Complexity

Slide 21

Slide 21 text

Complex vs Complicated

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

Complicated

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

Complex

Slide 26

Slide 26 text

Complex (even with perfect visibility)

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Complex system, an example

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

Addressing scalability issues

Slide 32

Slide 32 text

Issue 1: Fog of war

Slide 33

Slide 33 text

Bring in the typecheckers!

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

No content

Slide 36

Slide 36 text

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.

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

Issue 2: Complexity

Slide 39

Slide 39 text

Bring in FP!

Slide 40

Slide 40 text

Functional Programming 4 guides us towards predictability 4 allows us to reason locally and ignore context

Slide 41

Slide 41 text

In other terms...

Slide 42

Slide 42 text

Functional Programming makes problems complicated (as opposed to complex)

Slide 43

Slide 43 text

No content

Slide 44

Slide 44 text

Back to Earth...

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

No content

Slide 47

Slide 47 text

Add a typechecker

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

How?

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

What about FP? Where do I start from?

Slide 54

Slide 54 text

1. Stop using null and undefined

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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}';

Slide 58

Slide 58 text

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

Slide 60

Slide 60 text

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)

Slide 61

Slide 61 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

2. Stop throwing exceptions

Slide 70

Slide 70 text

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

Slide 72

Slide 72 text

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;

Slide 73

Slide 73 text

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);

Slide 74

Slide 74 text

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")

Slide 75

Slide 75 text

3. Stop using Promise

Slide 76

Slide 76 text

3. Stop using Promise Remember this? function question(message: string): Promise { // ... } const p = question("What is your name?"); const answer1 = await p; const answer2 = await p;

Slide 78

Slide 78 text

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>

Slide 79

Slide 79 text

What about (typed) errors?

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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"); }); }

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

Thank you!

Slide 84

Slide 84 text

Resources 4 https://gcanti.github.io/fp-ts/ 4 https://github.com/MostlyAdequate/mostly- adequate-guide 4 http://italiajs.herokuapp.com/ 4 channels #fp and #typescript

Slide 85

Slide 85 text

questions.d.ts @gabro27