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

How to get away with Functional Programming in ...

How to get away with Functional Programming in front-end applications

Functional programming is becoming pervasive in every branch of development, and if you're starting a new frontend project you can choose from many languages that allow you to embrace FP from the start.

However, we can rarely afford to rewrite apps from scratch. So, what to do when you want to start solving problems with functional programming in your existing JavaScript application?

In this presentation, we'll see how we can progressively migrate a codebase to TypeScript and start reaping the benefits of functional programming. You'll learn how to sneak FP into your code base, and get away with it!

Gabriele Petronella

October 25, 2018
Tweet

More Decks by Gabriele Petronella

Other Decks in Programming

Transcript

  1. Gabriele Petronella How to get away with FP in front-end

    applications Lambda World Cádiz 2018
  2. Programming with expressions Statement: let permissions = []; if (user.age

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

    18) List(generalPermission) else List(underagePermissions)
  4. 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
  5. 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
  6. 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
  7. Complicated 4 not simple 4 stll knowable 4 usually composed

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

    knowable 4 hard to predict accurately 4 cannot be taken apart and studied
  9. Complex Complex problems are tricker because: - their components behave

    differently depending on the global system behavior - adding/removing components affects the global system behavior
  10. 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.
  11. Flow 4 JS typechecker by Facebook 4 sophisticated type system

    with global inference 4 seems to be serving Facebook's needs
  12. TypeScript 4 Superset of Javascript by Microsoft 4 "pragmatic" type

    system with local inference 4 community oriented
  13. 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
  14. Make your JS code work with TS Option A Your

    project uses: - modern JavaScript - a transpiler (e.g. Babel) - a bundler (e.g. Webpack)
  15. 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 } }
  16. 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)
  17. 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 } }
  18. 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
  19. 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
  20. Writing definition files yourself Create a .d.ts file in your

    project declare module "mymodule" { export function someUtil<A>(a: A): Array<A>; }
  21. Writing definition files yourself import { someUtil } from 'mymodule';

    someUtil(2).map(x => x.length) // ^^^^^^ // error: property 'length' not found on type 'number'
  22. 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
  23. 1. Stop using null and undefined interface Device { getActiveCamera():

    Camera | undefined } interface Camera { getResolution(): Resolution | undefined } interface Resolution { width: number; height: number; }
  24. 1. Stop using null and undefined JavaScript optimistic version const

    width = device.getActiveCamera().getResolution().getWidth() // do something with width
  25. 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
  26. 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!)
  27. 2. 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
  28. 2. Stop using Promise How about this? function question(message: string):

    Task<string> { // ... } const p = question('What is your name?') p.chain(answer1 => p.map(answer2 => /* use answer1 and answer2 */) ).run()
  29. 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)
  30. 2. As a bonus, stop using async/await let result: number;

    try { result = await somePromise() } catch (e) { // handle the error result = 42 } // use 'result'
  31. 2. As a bonus, stop using async/await Use TaskEither from

    fp-ts someTaskEither() .mapLeft(e => 42) .map(result => { // use 'result' })