Slide 1

Slide 1 text

Gabriele Petronella How to get away with FP in front-end applications Lambda World Cádiz 2018

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: - their components behave differently depending on the global system behavior - 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

Yeah, but which one?

Slide 49

Slide 49 text

Flow 4 JS typechecker by Facebook 4 sophisticated type system with global inference 4 seems to be serving Facebook's needs

Slide 50

Slide 50 text

TypeScript 4 Superset of Javascript by Microsoft 4 "pragmatic" type system with local inference 4 community oriented

Slide 51

Slide 51 text

How?

Slide 52

Slide 52 text

Start small Make your existing JS code build with TS

Slide 53

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

Slide 54 text

Make your JS code work with TS Option A Your project uses: - modern JavaScript - a transpiler (e.g. Babel) - a bundler (e.g. Webpack)

Slide 55

Slide 55 text

Make your JS code work with TS (Option A) 4 replace the transpiler with TypeScript 4 tweak tsconfig.json to adapt to your existing code { "compilerOptions": { "target": "ES5", "allowJS": true, "allowSyntheticDefaultImports": true, "esModuleInterop": true } }

Slide 56

Slide 56 text

Make your JS code work with TS Option B Your project uses: - modern JavaScript - a transpiler (e.g. Babel) - transpiler plugins (e.g. Babel plugins) - a bundler (e.g. Webpack)

Slide 57

Slide 57 text

Make your JS code work with TS (Option B) 4 use TypeScript for typechecking only 4 pass the typechecked code to Babel { "compilerOptions": { "target": "esnext", "allowJS": true, "allowSyntheticDefaultImports": true } }

Slide 58

Slide 58 text

Caveat (for both options) 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 59

Slide 59 text

Actually using types 4 Write new files 4 Change the file extension from .js to .ts (or .tsx for JSX files) 4 Add typings for your dependencies from Definitely Typed npm install --save-dev @types/react

Slide 60

Slide 60 text

Writing definition files yourself Create a .d.ts file in your project declare module "mymodule" { export function someUtil(a: A): Array; }

Slide 61

Slide 61 text

Writing definition files yourself import { someUtil } from 'mymodule'; someUtil(2).map(x => x.length) // ^^^^^^ // error: property 'length' not found on type 'number'

Slide 62

Slide 62 text

Can this actually be done? Any proof?

Slide 63

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

Slide 64 text

What about FP? Where do I start from?

Slide 65

Slide 65 text

1. Stop using null and undefined

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

1. Stop using null and undefined JavaScript optimistic version const width = device.getActiveCamera().getResolution().getWidth() // do something with width

Slide 68

Slide 68 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.getWidth() } } // do something with width

Slide 69

Slide 69 text

Use Option instead device.getActiveCamera() .chain(camera => camera.getResolution()) .chain(resolution => resolution.getWidth()) .map(width => { // do something with width }) .getOrElse(/* fallback value */)

Slide 70

Slide 70 text

fp-ts Option and many other useful data structures are provided by fp-ts, a library for FP in TypeScript: https://github.com/gcanti/fp-ts (yes, it works even without HKT!)

Slide 71

Slide 71 text

2. Stop using Promise

Slide 72

Slide 72 text

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

Slide 73 text

2. Stop using Promise How about this? function question(message: string): Task { // ... } const p = question('What is your name?') p.chain(answer1 => p.map(answer2 => /* use answer1 and answer2 */) ).run()

Slide 74

Slide 74 text

2. Stop using Promise Task is a referentially transparent alternative to Promise. Another lazy alternative to Promise is IO from funfix- effect (a TypeScript library inspired by the Scala ecosystem)

Slide 75

Slide 75 text

2. As a bonus, stop using async/await let result: number; try { result = await somePromise() } catch (e) { // handle the error result = 42 } // use 'result'

Slide 76

Slide 76 text

2. As a bonus, stop using async/await Use TaskEither from fp-ts someTaskEither() .mapLeft(e => 42) .map(result => { // use 'result' })

Slide 77

Slide 77 text

Thank you!