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

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

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

    View full-size slide

  2. Programming with
    expressions

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  6. 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

    View full-size slide

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

    View full-size slide

  8. Referential transparency

    View full-size slide

  9. 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

    View full-size slide

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

    View full-size slide

  11. Let's talk about
    "scalability"

    View full-size slide

  12. Scaling issue #1
    Fog of war

    View full-size slide

  13. Scaling issue #2
    Complexity

    View full-size slide

  14. Complex vs Complicated

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  17. Complex (even with perfect visibility)

    View full-size slide

  18. Complex
    Complex problems are tricker because:
    - their components behave differently depending on the
    global system behavior
    - adding/removing components affects the global
    system behavior

    View full-size slide

  19. Complex system, an example

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  22. Addressing scalability
    issues

    View full-size slide

  23. Issue 1: Fog of war

    View full-size slide

  24. Bring in the typecheckers!

    View full-size slide

  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.

    View full-size slide

  26. Issue 2: Complexity

    View full-size slide

  27. Bring in FP!

    View full-size slide

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

    View full-size slide

  29. In other terms...

    View full-size slide

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

    View full-size slide

  31. Back to Earth...

    View full-size slide

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

    View full-size slide

  33. Add a typechecker

    View full-size slide

  34. Yeah, but which one?

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  37. Start small
    Make your existing JS code build with
    TS

    View full-size slide

  38. 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

    View full-size slide

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

    View full-size slide

  40. 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
    }
    }

    View full-size slide

  41. 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)

    View full-size slide

  42. 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
    }
    }

    View full-size slide

  43. 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

    View full-size slide

  44. 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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  47. Can this actually be done?
    Any proof?

    View full-size slide

  48. 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

    View full-size slide

  49. What about FP?
    Where do I start from?

    View full-size slide

  50. 1. Stop using null and
    undefined

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  53. 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

    View full-size slide

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

    View full-size slide

  55. 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!)

    View full-size slide

  56. 2. Stop using Promise

    View full-size slide

  57. 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

    View full-size slide

  58. 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()

    View full-size slide

  59. 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)

    View full-size slide

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

    View full-size slide

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

    View full-size slide