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

Connect.Tech 2019: Practical Functional Programming

Connect.Tech 2019: Practical Functional Programming

Functional programming is more than just math and monads. Functional programming empowers developers to solve real problems with safe, predictable, and maintainable code. In this talk, discover the basics of functional programming and how to apply functional concepts in a practical manner. You will learn how pure functions are easily testable, how to compose specialized functions to create more complex functions, how immutable data prevents bugs, how to prevent runtime errors with static types, how to safely model nulls with special types, and more! Most importantly, you will leave this talk with a deeper understanding and appreciation for functional programming so you can start exploring it yourself.

Jeremy Fairbank

October 18, 2019
Tweet

More Decks by Jeremy Fairbank

Other Decks in Programming

Transcript

  1. Practical
    Functional
    Programming
    Jeremy Fairbank
    @elpapapollo

    View full-size slide

  2. @testdouble helps improves
    how the world build software.
    testdouble.com

    View full-size slide

  3. Available in
    print or e-book
    programming-elm.com

    View full-size slide

  4. Functional
    programming:

    View full-size slide

  5. Pure
    Functional
    programming:

    View full-size slide

  6. Pure
    Total
    Functional
    programming:

    View full-size slide

  7. Pure
    Total
    Idempotent
    Functional
    programming:

    View full-size slide

  8. Pure
    Total
    Idempotent
    Monad
    Functional
    programming:

    View full-size slide

  9. Pure
    Total
    Idempotent
    Monad
    Functor
    Functional
    programming:

    View full-size slide

  10. Easier-to-reason-about-
    programming

    View full-size slide

  11. Pure
    Total
    Idempotent
    Monad
    Functor

    View full-size slide

  12. 6
    Become a rockstar
    programmer with these
    tricks!

    View full-size slide

  13. Hard to test



    View full-size slide

  14. async function printOrderDetails(ids) {
    let url = `/orders?ids=${ids.join(',')}`
    let orders = await fetchJson(url)
    for (let i = 0; i < orders.length; i++) {
    let { id, date, customer } = orders[i]
    let details = `Order ${id} on ${date} by ${customer}`
    console.log(details)
    }
    }

    View full-size slide

  15. async function printOrderDetails(ids) {
    let url = `/orders?ids=${ids.join(',')}`
    let orders = await fetchJson(url)
    for (let i = 0; i < orders.length; i++) {
    let { id, date, customer } = orders[i]
    let details = `Order ${id} on ${date} by ${customer}`
    console.log(details)
    }
    }

    View full-size slide

  16. async function printOrderDetails(ids) {
    let url = `/orders?ids=${ids.join(',')}`
    let orders = await fetchJson(url)
    for (let i = 0; i < orders.length; i++) {
    let { id, date, customer } = orders[i]
    let details = `Order ${id} on ${date} by ${customer}`
    console.log(details)
    }
    }

    View full-size slide

  17. async function printOrderDetails(ids) {
    let url = `/orders?ids=${ids.join(',')}`
    let orders = await fetchJson(url)
    for (let i = 0; i < orders.length; i++) {
    let { id, date, customer } = orders[i]
    let details = `Order ${id} on ${date} by ${customer}`
    console.log(details)
    }
    }

    View full-size slide

  18. async function printOrderDetails(ids) {
    let url = `/orders?ids=${ids.join(',')}`
    let orders = await fetchJson(url)
    for (let i = 0; i < orders.length; i++) {
    let { id, date, customer } = orders[i]
    let details = `Order ${id} on ${date} by ${customer}`
    console.log(details)
    }
    }

    View full-size slide

  19. const td = require('testdouble')
    describe('order details', () => {
    let originalLog = console.log
    let printOrderDetails
    let fetchJson
    beforeEach(() => {
    fetchJson = td.replace('./fetchJson')
    console.log = td.function('console.log')
    printOrderDetails = require('./printOrderDetails')
    })
    afterEach(() => {
    td.reset()
    console.log = originalLog
    })

    View full-size slide

  20. const td = require('testdouble')
    describe('order details', () => {
    let originalLog = console.log
    let printOrderDetails
    let fetchJson
    beforeEach(() => {
    fetchJson = td.replace('./fetchJson')
    console.log = td.function('console.log')
    printOrderDetails = require('./printOrderDetails')
    })
    afterEach(() => {
    td.reset()
    console.log = originalLog
    })

    View full-size slide

  21. const td = require('testdouble')
    describe('order details', () => {
    let originalLog = console.log
    let printOrderDetails
    let fetchJson
    beforeEach(() => {
    fetchJson = td.replace('./fetchJson')
    console.log = td.function('console.log')
    printOrderDetails = require('./printOrderDetails')
    })
    afterEach(() => {
    td.reset()
    console.log = originalLog
    })

    View full-size slide

  22. const td = require('testdouble')
    describe('order details', () => {
    let originalLog = console.log
    let printOrderDetails
    let fetchJson
    beforeEach(() => {
    fetchJson = td.replace('./fetchJson')
    console.log = td.function('console.log')
    printOrderDetails = require('./printOrderDetails')
    })
    afterEach(() => {
    td.reset()
    console.log = originalLog
    })

    View full-size slide

  23. it('prints details for multiple orders', async () => {
    td
    .when(fetchJson('/orders?ids=1,2,3'))
    .thenResolve([
    { id: 1, date: '12/1/17', customer: 'Tucker' },
    { id: 2, date: '11/25/17', customer: 'Sally' },
    { id: 3, date: '3/30/18', customer: 'Joe' },
    ])
    await printOrderDetails([1, 2, 3])
    td.verify(console.log('Order 1 on 12/1/17 by Tucker'))
    td.verify(console.log('Order 2 on 11/25/17 by Sally'))
    td.verify(console.log('Order 3 on 3/30/18 by Joe'))
    })
    })

    View full-size slide

  24. it('prints details for multiple orders', async () => {
    td
    .when(fetchJson('/orders?ids=1,2,3'))
    .thenResolve([
    { id: 1, date: '12/1/17', customer: 'Tucker' },
    { id: 2, date: '11/25/17', customer: 'Sally' },
    { id: 3, date: '3/30/18', customer: 'Joe' },
    ])
    await printOrderDetails([1, 2, 3])
    td.verify(console.log('Order 1 on 12/1/17 by Tucker'))
    td.verify(console.log('Order 2 on 11/25/17 by Sally'))
    td.verify(console.log('Order 3 on 3/30/18 by Joe'))
    })
    })

    View full-size slide

  25. it('prints details for multiple orders', async () => {
    td
    .when(fetchJson('/orders?ids=1,2,3'))
    .thenResolve([
    { id: 1, date: '12/1/17', customer: 'Tucker' },
    { id: 2, date: '11/25/17', customer: 'Sally' },
    { id: 3, date: '3/30/18', customer: 'Joe' },
    ])
    await printOrderDetails([1, 2, 3])
    td.verify(console.log('Order 1 on 12/1/17 by Tucker'))
    td.verify(console.log('Order 2 on 11/25/17 by Sally'))
    td.verify(console.log('Order 3 on 3/30/18 by Joe'))
    })
    })

    View full-size slide

  26. it('prints details for multiple orders', async () => {
    td
    .when(fetchJson('/orders?ids=1,2,3'))
    .thenResolve([
    { id: 1, date: '12/1/17', customer: 'Tucker' },
    { id: 2, date: '11/25/17', customer: 'Sally' },
    { id: 3, date: '3/30/18', customer: 'Joe' },
    ])
    await printOrderDetails([1, 2, 3])
    td.verify(console.log('Order 1 on 12/1/17 by Tucker'))
    td.verify(console.log('Order 2 on 11/25/17 by Sally'))
    td.verify(console.log('Order 3 on 3/30/18 by Joe'))
    })
    })

    View full-size slide

  27. Side Effects

    View full-size slide

  28. async function printOrderDetails(ids) {
    let url = `/orders?ids=${ids.join(',')}`
    let orders = await fetchJson(url)
    for (let i = 0; i < orders.length; i++) {
    let { id, date, customer } = orders[i]
    let details = `Order ${id} on ${date} by ${customer}`
    console.log(details)
    }
    }

    View full-size slide

  29. async function printOrderDetails(ids) {
    let url = `/orders?ids=${ids.join(',')}`
    let orders = await fetchJson(url)
    for (let i = 0; i < orders.length; i++) {
    let { id, date, customer } = orders[i]
    let details = `Order ${id} on ${date} by ${customer}`
    console.log(details)
    }
    }

    View full-size slide

  30. async function printOrderDetails(ids) {
    let url = `/orders?ids=${ids.join(',')}`
    let orders = await fetchJson(url)
    for (let i = 0; i < orders.length; i++) {
    let { id, date, customer } = orders[i]
    let details = `Order ${id} on ${date} by ${customer}`
    console.log(details)
    }
    }

    View full-size slide

  31. Less Predictable

    View full-size slide

  32. Harder to Test

    View full-size slide

  33. const add = (x, y) => x + y
    add(2, 3) === 5
    add(2, 3) === 5
    add(2, 3) === 5

    View full-size slide

  34. const url = ids => `/orders?ids=${ids.join(',')}`
    const orderDetails = ({ id, date, customer }) =>
    `Order ${id} on ${date} by ${customer}`
    function detailsForOrders(orders) {
    let details = []
    for (let i = 0; i < orders.length; i++) {
    details.push(orderDetails(orders[i]))
    }
    return details
    }

    View full-size slide

  35. const url = ids => `/orders?ids=${ids.join(',')}`
    const orderDetails = ({ id, date, customer }) =>
    `Order ${id} on ${date} by ${customer}`
    function detailsForOrders(orders) {
    let details = []
    for (let i = 0; i < orders.length; i++) {
    details.push(orderDetails(orders[i]))
    }
    return details
    }

    View full-size slide

  36. const url = ids => `/orders?ids=${ids.join(',')}`
    const orderDetails = ({ id, date, customer }) =>
    `Order ${id} on ${date} by ${customer}`
    function detailsForOrders(orders) {
    let details = []
    for (let i = 0; i < orders.length; i++) {
    details.push(orderDetails(orders[i]))
    }
    return details
    }

    View full-size slide

  37. const url = ids => `/orders?ids=${ids.join(',')}`
    const orderDetails = ({ id, date, customer }) =>
    `Order ${id} on ${date} by ${customer}`
    function detailsForOrders(orders) {
    let details = []
    for (let i = 0; i < orders.length; i++) {
    details.push(orderDetails(orders[i]))
    }
    return details
    }

    View full-size slide

  38. const url = ids => `/orders?ids=${ids.join(',')}`
    const orderDetails = ({ id, date, customer }) =>
    `Order ${id} on ${date} by ${customer}`
    function detailsForOrders(orders) {
    let details = []
    for (let i = 0; i < orders.length; i++) {
    details.push(orderDetails(orders[i]))
    }
    return details
    }

    View full-size slide

  39. const url = ids => `/orders?ids=${ids.join(',')}`
    const orderDetails = ({ id, date, customer }) =>
    `Order ${id} on ${date} by ${customer}`
    function detailsForOrders(orders) {
    let details = []
    for (let i = 0; i < orders.length; i++) {
    details.push(orderDetails(orders[i]))
    }
    return details
    }

    View full-size slide

  40. const url = ids => `/orders?ids=${ids.join(',')}`
    const orderDetails = ({ id, date, customer }) =>
    `Order ${id} on ${date} by ${customer}`
    function detailsForOrders(orders) {
    let details = []
    for (let i = 0; i < orders.length; i++) {
    details.push(orderDetails(orders[i]))
    }
    return details
    }

    View full-size slide

  41. it('uses the correct url', () => {
    expect(url([1, 2, 3])).toEqual('/orders?ids=1,2,3')
    })

    View full-size slide

  42. it('creates details for orders', () => {
    let orders = [
    { id: 1, date: '12/1/17', customer: 'Tucker' },
    { id: 2, date: '11/25/17', customer: 'Sally' },
    { id: 3, date: '3/30/18', customer: 'Joe' },
    ]
    expect(detailsForOrders(orders)).toEqual([
    'Order 1 on 12/1/17 by Tucker',
    'Order 2 on 11/25/17 by Sally',
    'Order 3 on 3/30/18 by Joe',
    ])
    })

    View full-size slide

  43. async function printOrderDetails(ids) {
    let orders = await fetchJson(url(ids))
    let details = detailsForOrders(orders)
    for (let i = 0; i < details.length; i++) {
    console.log(details[i])
    }
    }

    View full-size slide

  44. async function printOrderDetails(ids) {
    let orders = await fetchJson(url(ids))
    let details = detailsForOrders(orders)
    for (let i = 0; i < details.length; i++) {
    console.log(details[i])
    }
    }

    View full-size slide

  45. async function printOrderDetails(ids) {
    let orders = await fetchJson(url(ids))
    let details = detailsForOrders(orders)
    for (let i = 0; i < details.length; i++) {
    console.log(details[i])
    }
    }

    View full-size slide

  46. async function printOrderDetails(ids) {
    let orders = await fetchJson(url(ids))
    let details = detailsForOrders(orders)
    for (let i = 0; i < details.length; i++) {
    console.log(details[i])
    }
    }

    View full-size slide

  47. youtu.be/yTkzNHF6rMs

    View full-size slide

  48. Hard to test Hard to follow


    View full-size slide

  49. const orderDetails = ({ id, date, customer }) =>
    `Order ${id} on ${date} by ${customer}`
    function detailsForOrders(orders) {
    let details = []
    for (let i = 0; i < orders.length; i++) {
    details.push(orderDetails(orders[i]))
    }
    return details
    }

    View full-size slide

  50. const orderDetails = ({ id, date, customer }) =>
    `Order ${id} on ${date} by ${customer}`
    function detailsForOrders(orders) {
    let details = []
    for (let i = 0; i < orders.length; i++) {
    details.push(orderDetails(orders[i]))
    }
    return details
    }

    View full-size slide

  51. const orderDetails = ({ id, date, customer }) =>
    `Order ${id} on ${date} by ${customer}`
    const detailsForOrders = orders =>
    orders.map(orderDetails)

    View full-size slide

  52. const orderDetails = ({ id, date, customer }) =>
    `Order ${id} on ${date} by ${customer}`
    const detailsForOrders = orders =>
    orders.map(orderDetails)

    View full-size slide

  53. { id: 1, date: '12/1/17', customer: 'Tucker' }
    { id: 2, date: '11/25/17', customer: 'Sally' }
    { id: 3, date: '3/30/18', customer: 'Joe' }

    View full-size slide

  54. { id: 1, date: '12/1/17', customer: 'Tucker' }
    { id: 2, date: '11/25/17', customer: 'Sally' }
    { id: 3, date: '3/30/18', customer: 'Joe' }
    [
    'Order 1 on 12/1/17 by Tucker',
    ]
    map
    orderDetails

    View full-size slide

  55. [
    'Order 1 on 12/1/17 by Tucker',
    'Order 2 on 11/25/17 by Sally',
    ]
    orderDetails
    { id: 1, date: '12/1/17', customer: 'Tucker' }
    { id: 2, date: '11/25/17', customer: 'Sally' }
    { id: 3, date: '3/30/18', customer: 'Joe' }
    map

    View full-size slide

  56. orderDetails
    [
    'Order 1 on 12/1/17 by Tucker',
    'Order 2 on 11/25/17 by Sally',
    'Order 3 on 3/30/18 by Joe',
    ]
    map
    { id: 1, date: '12/1/17', customer: 'Tucker' }
    { id: 2, date: '11/25/17', customer: 'Sally' }
    { id: 3, date: '3/30/18', customer: 'Joe' }

    View full-size slide

  57. Hard to test Hard to follow
    Code breaks
    unexpectedly


    View full-size slide

  58. class Person {
    constructor(name, hobbies) {
    this.name = name
    this.hobbies = hobbies
    }
    describe() {
    if (this.hobbies.length === 0) {
    return `${this.name} doesn't do much`
    }
    let hobbies = this.hobbies.splice(0, 2).join(' and ')
    return `${this.name} likes to do things such as ${hobbies}`
    }
    }

    View full-size slide

  59. class Person {
    constructor(name, hobbies) {
    this.name = name
    this.hobbies = hobbies
    }
    describe() {
    if (this.hobbies.length === 0) {
    return `${this.name} doesn't do much`
    }
    let hobbies = this.hobbies.splice(0, 2).join(' and ')
    return `${this.name} likes to do things such as ${hobbies}`
    }
    }

    View full-size slide

  60. class Person {
    constructor(name, hobbies) {
    this.name = name
    this.hobbies = hobbies
    }
    describe() {
    if (this.hobbies.length === 0) {
    return `${this.name} doesn't do much`
    }
    let hobbies = this.hobbies.splice(0, 2).join(' and ')
    return `${this.name} likes to do things such as ${hobbies}`
    }
    }

    View full-size slide

  61. class Person {
    constructor(name, hobbies) {
    this.name = name
    this.hobbies = hobbies
    }
    describe() {
    if (this.hobbies.length === 0) {
    return `${this.name} doesn't do much`
    }
    let hobbies = this.hobbies.splice(0, 2).join(' and ')
    return `${this.name} likes to do things such as ${hobbies}`
    }
    }

    View full-size slide

  62. class Person {
    constructor(name, hobbies) {
    this.name = name
    this.hobbies = hobbies
    }
    describe() {
    if (this.hobbies.length === 0) {
    return `${this.name} doesn't do much`
    }
    let hobbies = this.hobbies.splice(0, 2).join(' and ')
    return `${this.name} likes to do things such as ${hobbies}`
    }
    }

    View full-size slide

  63. class Person {
    constructor(name, hobbies) {
    this.name = name
    this.hobbies = hobbies
    }
    describe() {
    if (this.hobbies.length === 0) {
    return `${this.name} doesn't do much`
    }
    let hobbies = this.hobbies.splice(0, 2).join(' and ')
    return `${this.name} likes to do things such as ${hobbies}`
    }
    }

    View full-size slide

  64. let person = new Person('Jeremy', [
    'programming',
    'reading',
    'playing music',
    ])
    person.describe()
    // Jeremy likes to do things such as programming and reading
    person.describe()
    // Jeremy likes to do things such as playing music

    View full-size slide

  65. let person = new Person('Jeremy', [
    'programming',
    'reading',
    'playing music',
    ])
    person.describe()
    // Jeremy likes to do things such as programming and reading
    person.describe()
    // Jeremy likes to do things such as playing music

    View full-size slide

  66. let person = new Person('Jeremy', [
    'programming',
    'reading',
    'playing music',
    ])
    person.describe()
    // Jeremy likes to do things such as programming and reading
    person.describe()
    // Jeremy likes to do things such as playing music

    View full-size slide

  67. let person = new Person('Jeremy', [
    'programming',
    'reading',
    'playing music',
    ])
    person.describe()
    // Jeremy likes to do things such as programming and reading
    person.describe()
    // Jeremy likes to do things such as playing music

    View full-size slide

  68. let hobbies = this.hobbies.splice(0, 2).join(' and ')

    View full-size slide

  69. Mutable Data

    View full-size slide

  70. Immutable Data
    Safety and consistency

    View full-size slide

  71. class Person {
    constructor(name, hobbies) {
    this.name = name
    this.hobbies = Object.freeze(hobbies)
    }
    // ...
    }

    View full-size slide

  72. class Person {
    // ...
    describe() {
    // ...
    let hobbies = this.hobbies.splice(0, 2).join(' and ')
    // ...
    }
    }

    View full-size slide

  73. class Person {
    // ...
    describe() {
    // ...
    let hobbies = this.hobbies.splice(0, 2).join(' and ')
    // ...
    }
    }
    TypeError

    View full-size slide

  74. class Person {
    // ...
    describe() {
    // ...
    let hobbies = this.hobbies.slice(0, 2).join(' and ')
    // ...
    }
    }

    View full-size slide

  75. Create New State

    View full-size slide

  76. const create = (name, hobbies) =>
    Object.freeze({ name, hobbies: Object.freeze(hobbies) })
    function describe(person) {
    if (person.hobbies.length === 0) {
    return `${person.name} doesn't do much`
    }
    let hobbies = person.hobbies.slice(0, 2).join(' and ')
    return `${person.name} likes to do things such as ${hobbies}`
    }

    View full-size slide

  77. const create = (name, hobbies) =>
    Object.freeze({ name, hobbies: Object.freeze(hobbies) })
    function describe(person) {
    if (person.hobbies.length === 0) {
    return `${person.name} doesn't do much`
    }
    let hobbies = person.hobbies.slice(0, 2).join(' and ')
    return `${person.name} likes to do things such as ${hobbies}`
    }

    View full-size slide

  78. const create = (name, hobbies) =>
    Object.freeze({ name, hobbies: Object.freeze(hobbies) })
    function describe(person) {
    if (person.hobbies.length === 0) {
    return `${person.name} doesn't do much`
    }
    let hobbies = person.hobbies.slice(0, 2).join(' and ')
    return `${person.name} likes to do things such as ${hobbies}`
    }

    View full-size slide

  79. const addHobby = (hobby, person) =>
    Object.freeze({
    ...person,
    hobbies: Object.freeze([...person.hobbies, hobby]),
    })

    View full-size slide

  80. const addHobby = (hobby, person) =>
    Object.freeze({
    ...person,
    hobbies: Object.freeze([...person.hobbies, hobby]),
    })

    View full-size slide

  81. const addHobby = (hobby, person) =>
    Object.freeze({
    ...person,
    hobbies: Object.freeze([...person.hobbies, hobby]),
    })

    View full-size slide

  82. const addHobby = (hobby, person) =>
    Object.freeze({
    ...person,
    hobbies: Object.freeze([...person.hobbies, hobby]),
    })

    View full-size slide

  83. const addHobby = (hobby, person) =>
    Object.freeze({
    ...person,
    hobbies: Object.freeze([...person.hobbies, hobby]),
    })

    View full-size slide

  84. let person = create('Jeremy', [])
    person = addHobby('programming', person)
    person = addHobby('reading', person)
    describe(person)
    // Jeremy likes to do things such as programming and reading

    View full-size slide

  85. let person = create('Jeremy', [])
    person = addHobby('programming', person)
    person = addHobby('reading', person)
    describe(person)
    // Jeremy likes to do things such as programming and reading

    View full-size slide

  86. let person = create('Jeremy', [])
    person = addHobby('programming', person)
    person = addHobby('reading', person)
    describe(person)
    // Jeremy likes to do things such as programming and reading

    View full-size slide

  87. let person = create('Jeremy', [])
    person = addHobby('programming', person)
    person = addHobby('reading', person)
    describe(person)
    // Jeremy likes to do things such as programming and reading

    View full-size slide

  88. let person = create('Jeremy', [])
    person = addHobby('programming', person)
    person = addHobby('reading', person)
    describe(person)
    // Jeremy likes to do things such as programming and reading

    View full-size slide

  89. Hard to test Hard to follow
    Code breaks
    unexpectedly
    Too much code

    View full-size slide

  90. const greetInEnglish = name => `Hi, ${name}`
    const greetInSpanish = name => `Hola, ${name}`

    View full-size slide

  91. const greet = (greeting, name) => `${greeting}, ${name}`
    // ..
    const greetInEnglish = name => greet('Hi', name)
    const greetInSpanish = name => greet('Hola', name)

    View full-size slide

  92. const greet = greeting => name => `${greeting}, ${name}`
    const greetInEnglish = greet('Hi')
    const greetInSpanish = greet('Hola')
    greetInEnglish('Connect.Tech')
    // Hi, Connect.Tech
    greetInSpanish('Connect.Tech')
    // Hola, Connect.Tech

    View full-size slide

  93. const greet = greeting => name => `${greeting}, ${name}`
    const greetInEnglish = greet('Hi')
    const greetInSpanish = greet('Hola')
    greetInEnglish('Connect.Tech')
    // Hi, Connect.Tech
    greetInSpanish('Connect.Tech')
    // Hola, Connect.Tech

    View full-size slide

  94. const greet = greeting => name => `${greeting}, ${name}`
    const greetInEnglish = greet('Hi')
    const greetInSpanish = greet('Hola')
    greetInEnglish('Connect.Tech')
    // Hi, Connect.Tech
    greetInSpanish('Connect.Tech')
    // Hola, Connect.Tech

    View full-size slide

  95. const greet = greeting => name => `${greeting}, ${name}`
    const greetInEnglish = greet('Hi')
    const greetInSpanish = greet('Hola')
    greetInEnglish('Connect.Tech')
    // Hi, Connect.Tech
    greetInSpanish('Connect.Tech')
    // Hola, Connect.Tech

    View full-size slide

  96. const greet = greeting => name => `${greeting}, ${name}`
    const greetInEnglish = greet('Hi')
    const greetInSpanish = greet('Hola')
    greetInEnglish('Connect.Tech')
    // Hi, Connect.Tech
    greetInSpanish('Connect.Tech')
    // Hola, Connect.Tech

    View full-size slide

  97. const greet = greeting => name => `${greeting}, ${name}`
    const greetInEnglish = greet('Hi')
    const greetInSpanish = greet('Hola')
    greetInEnglish('Connect.Tech')
    // Hi, Connect.Tech
    greetInSpanish('Connect.Tech')
    // Hola, Connect.Tech

    View full-size slide

  98. const greet = greeting => name => `${greeting}, ${name}`
    const greetInEnglish = greet('Hi')
    const greetInSpanish = greet('Hola')
    greetInEnglish('Connect.Tech')
    // Hi, Connect.Tech
    greetInSpanish('Connect.Tech')
    // Hola, Connect.Tech

    View full-size slide

  99. const greet = greeting => name => `${greeting}, ${name}`
    const greetInEnglish = greet('Hi')
    const greetInSpanish = greet('Hola')
    greetInEnglish('Connect.Tech')
    // Hi, Connect.Tech
    greetInSpanish('Connect.Tech')
    // Hola, Connect.Tech

    View full-size slide

  100. const greet = greeting => name => `${greeting}, ${name}`
    const greetInEnglish = greet('Hi')
    const greetInSpanish = greet('Hola')
    Curried Function
    Partial Application

    View full-size slide

  101. greet greeting name =
    greeting ++ ", " ++ name
    greetInEnglish = greet "Hi"
    greetInSpanish = greet "Hola"

    View full-size slide

  102. greet greeting name =
    greeting ++ ", " ++ name
    greetInEnglish = greet "Hi"
    greetInSpanish = greet "Hola"

    View full-size slide

  103. greet greeting name =
    greeting ++ ", " ++ name
    greetInEnglish = greet "Hi"
    greetInSpanish = greet "Hola"

    View full-size slide

  104. greet greeting name =
    greeting ++ ", " ++ name
    greetInEnglish = greet "Hi"
    greetInSpanish = greet "Hola"

    View full-size slide

  105. greetInEnglish "Connect.Tech"
    -- Hi, Connect.Tech
    greetInSpanish "Connect.Tech"
    -- Hola, Connect.Tech

    View full-size slide

  106. function createOrder(shoppingCart, customer) {
    let total = 0
    shoppingCart.items.forEach(item => {
    total += item.price
    })
    shoppingCart.discounts.forEach(discount => {
    total *= 1 - discount
    })
    total *= 1 + shoppingCart.tax
    total += shoppingCart.shippingCost
    total = Math.round(total * 100) / 100
    return { status: 'new', customer, total }
    }

    View full-size slide

  107. function createOrder(shoppingCart, customer) {
    let total = 0
    shoppingCart.items.forEach(item => {
    total += item.price
    })
    shoppingCart.discounts.forEach(discount => {
    total *= 1 - discount
    })
    total *= 1 + shoppingCart.tax
    total += shoppingCart.shippingCost
    total = Math.round(total * 100) / 100
    return { status: 'new', customer, total }
    }

    View full-size slide

  108. function createOrder(shoppingCart, customer) {
    let total = 0
    shoppingCart.items.forEach(item => {
    total += item.price
    })
    shoppingCart.discounts.forEach(discount => {
    total *= 1 - discount
    })
    total *= 1 + shoppingCart.tax
    total += shoppingCart.shippingCost
    total = Math.round(total * 100) / 100
    return { status: 'new', customer, total }
    }

    View full-size slide

  109. function createOrder(shoppingCart, customer) {
    let total = 0
    shoppingCart.items.forEach(item => {
    total += item.price
    })
    shoppingCart.discounts.forEach(discount => {
    total *= 1 - discount
    })
    total *= 1 + shoppingCart.tax
    total += shoppingCart.shippingCost
    total = Math.round(total * 100) / 100
    return { status: 'new', customer, total }
    }

    View full-size slide

  110. function createOrder(shoppingCart, customer) {
    let total = 0
    shoppingCart.items.forEach(item => {
    total += item.price
    })
    shoppingCart.discounts.forEach(discount => {
    total *= 1 - discount
    })
    total *= 1 + shoppingCart.tax
    total += shoppingCart.shippingCost
    total = Math.round(total * 100) / 100
    return { status: 'new', customer, total }
    }

    View full-size slide

  111. function createOrder(shoppingCart, customer) {
    let total = 0
    shoppingCart.items.forEach(item => {
    total += item.price
    })
    shoppingCart.discounts.forEach(discount => {
    total *= 1 - discount
    })
    total *= 1 + shoppingCart.tax
    total += shoppingCart.shippingCost
    total = Math.round(total * 100) / 100
    return { status: 'new', customer, total }
    }

    View full-size slide

  112. function createOrder(shoppingCart, customer) {
    let total = 0
    shoppingCart.items.forEach(item => {
    total += item.price
    })
    shoppingCart.discounts.forEach(discount => {
    total *= 1 - discount
    })
    total *= 1 + shoppingCart.tax
    total += shoppingCart.shippingCost
    total = Math.round(total * 100) / 100
    return { status: 'new', customer, total }
    }

    View full-size slide

  113. function createOrder(shoppingCart, customer) {
    let total = 0
    shoppingCart.items.forEach(item => {
    total += item.price
    })
    shoppingCart.discounts.forEach(discount => {
    total *= 1 - discount
    })
    total *= 1 + shoppingCart.tax
    total += shoppingCart.shippingCost
    total = Math.round(total * 100) / 100
    return { status: 'new', customer, total }
    }

    View full-size slide

  114. const excitedGreeting = name =>
    // ...

    View full-size slide

  115. const toUpper = string => string.toUpperCase()
    const greet = greeting => name => `${greeting}, ${name}`
    const exclaim = phrase => `${phrase}!`
    const excitedGreeting = name =>
    exclaim(greet('Hi')(toUpper(name)))
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  116. const toUpper = string => string.toUpperCase()
    const greet = greeting => name => `${greeting}, ${name}`
    const exclaim = phrase => `${phrase}!`
    const excitedGreeting = name =>
    exclaim(greet('Hi')(toUpper(name)))
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  117. const toUpper = string => string.toUpperCase()
    const greet = greeting => name => `${greeting}, ${name}`
    const exclaim = phrase => `${phrase}!`
    const excitedGreeting = name =>
    exclaim(greet('Hi')(toUpper(name)))
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  118. const toUpper = string => string.toUpperCase()
    const greet = greeting => name => `${greeting}, ${name}`
    const exclaim = phrase => `${phrase}!`
    const excitedGreeting = name =>
    exclaim(greet('Hi')(toUpper(name)))
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  119. const toUpper = string => string.toUpperCase()
    const greet = greeting => name => `${greeting}, ${name}`
    const exclaim = phrase => `${phrase}!`
    const excitedGreeting = name =>
    exclaim(greet('Hi')(toUpper(name)))
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  120. const toUpper = string => string.toUpperCase()
    const greet = greeting => name => `${greeting}, ${name}`
    const exclaim = phrase => `${phrase}!`
    const excitedGreeting = name =>
    exclaim(greet('Hi')(toUpper(name)))
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  121. const toUpper = string => string.toUpperCase()
    const greet = greeting => name => `${greeting}, ${name}`
    const exclaim = phrase => `${phrase}!`
    const excitedGreeting = name =>
    exclaim(greet('Hi')(toUpper(name)))
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  122. const toUpper = string => string.toUpperCase()
    const greet = greeting => name => `${greeting}, ${name}`
    const exclaim = phrase => `${phrase}!`
    const excitedGreeting = name =>
    exclaim(greet('Hi')(toUpper(name)))
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  123. const R = require('ramda')
    const excitedGreeting = name =>
    R.pipe(
    toUpper,
    greet('Hi'),
    exclaim,
    )(name)
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  124. const R = require('ramda')
    const excitedGreeting = name =>
    R.pipe(
    toUpper,
    greet('Hi'),
    exclaim,
    )(name)
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  125. const R = require('ramda')
    const excitedGreeting = name =>
    R.pipe(
    toUpper,
    greet('Hi'),
    exclaim,
    )(name)
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  126. const R = require('ramda')
    const excitedGreeting = name =>
    R.pipe(
    toUpper,
    greet('Hi'),
    exclaim,
    )(name)
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  127. const R = require('ramda')
    const excitedGreeting = name =>
    R.pipe(
    toUpper, // CONNECT.TECH
    greet('Hi'),
    exclaim,
    )(name)
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  128. const R = require('ramda')
    const excitedGreeting = name =>
    R.pipe(
    toUpper,
    greet('Hi'), // Hi, CONNECT.TECH
    exclaim,
    )(name)
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  129. const R = require('ramda')
    const excitedGreeting = name =>
    R.pipe(
    toUpper,
    greet('Hi'),
    exclaim, // Hi, CONNECT.TECH!
    )(name)
    excitedGreeting('Connect.Tech')
    // Hi, CONNECT.TECH!

    View full-size slide

  130. Function
    Composition

    View full-size slide

  131. greet greeting name =
    greeting ++ ", " ++ name
    exclaim phrase =
    phrase ++ "!"
    excitedGreeting name =
    name
    |> String.toUpper
    |> greet "Hi"
    |> exclaim
    excitedGreeting "Connect.Tech"
    -- Hi, CONNECT.TECH!

    View full-size slide

  132. greet greeting name =
    greeting ++ ", " ++ name
    exclaim phrase =
    phrase ++ "!"
    excitedGreeting name =
    name
    |> String.toUpper
    |> greet "Hi"
    |> exclaim
    excitedGreeting "Connect.Tech"
    -- Hi, CONNECT.TECH!

    View full-size slide

  133. greet greeting name =
    greeting ++ ", " ++ name
    exclaim phrase =
    phrase ++ "!"
    excitedGreeting name =
    name
    |> String.toUpper
    |> greet "Hi"
    |> exclaim
    excitedGreeting "Connect.Tech"
    -- Hi, CONNECT.TECH!

    View full-size slide

  134. greet greeting name =
    greeting ++ ", " ++ name
    exclaim phrase =
    phrase ++ "!"
    excitedGreeting name =
    name
    |> String.toUpper
    |> greet "Hi"
    |> exclaim
    excitedGreeting "Connect.Tech"
    -- Hi, CONNECT.TECH!

    View full-size slide

  135. greet greeting name =
    greeting ++ ", " ++ name
    exclaim phrase =
    phrase ++ "!"
    excitedGreeting name =
    name
    |> String.toUpper -- CONNECT.TECH
    |> greet "Hi"
    |> exclaim
    excitedGreeting “Connect.Tech"
    -- Hi, CONNECT.TECH!

    View full-size slide

  136. greet greeting name =
    greeting ++ ", " ++ name
    exclaim phrase =
    phrase ++ "!"
    excitedGreeting name =
    name
    |> String.toUpper
    |> greet "Hi" -- Hi, CONNECT.TECH
    |> exclaim
    excitedGreeting "Connect.Tech"
    -- Hi, CONNECT.TECH!

    View full-size slide

  137. greet greeting name =
    greeting ++ ", " ++ name
    exclaim phrase =
    phrase ++ "!"
    excitedGreeting name =
    name
    |> String.toUpper
    |> greet "Hi"
    |> exclaim -- Hi, CONNECT.TECH!
    excitedGreeting "Connect.Tech"
    -- Hi, CONNECT.TECH!

    View full-size slide

  138. function createOrder(shoppingCart, customer) {
    let total = 0
    shoppingCart.items.forEach(item => {
    total += item.price
    })
    shoppingCart.discounts.forEach(discount => {
    total *= 1 - discount
    })
    total *= 1 + shoppingCart.tax
    total += shoppingCart.shippingCost
    total = Math.round(total * 100) / 100
    return { status: 'new', customer, total }
    }

    View full-size slide

  139. function createOrder(shoppingCart, customer) {
    let total = R.pipe(
    addPrices(shoppingCart.items),
    addDiscounts(shoppingCart.discounts),
    addTax(shoppingCart.tax),
    add(shoppingCart.shippingCost),
    roundTotal,
    )(0)
    return { status: 'new', customer, total }
    }

    View full-size slide

  140. function createOrder(shoppingCart, customer) {
    let total = R.pipe(
    addPrices(shoppingCart.items),
    addDiscounts(shoppingCart.discounts),
    addTax(shoppingCart.tax),
    add(shoppingCart.shippingCost),
    roundTotal,
    )(0)
    return { status: 'new', customer, total }
    }

    View full-size slide

  141. function createOrder(shoppingCart, customer) {
    let total = R.pipe(
    addPrices(shoppingCart.items),
    addDiscounts(shoppingCart.discounts),
    addTax(shoppingCart.tax),
    add(shoppingCart.shippingCost),
    roundTotal,
    )(0)
    return { status: 'new', customer, total }
    }

    View full-size slide

  142. function createOrder(shoppingCart, customer) {
    let total = R.pipe(
    addPrices(shoppingCart.items),
    addDiscounts(shoppingCart.discounts),
    addTax(shoppingCart.tax),
    add(shoppingCart.shippingCost),
    roundTotal,
    )(0)
    return { status: 'new', customer, total }
    }

    View full-size slide

  143. Hard to test Hard to follow
    Code breaks
    unexpectedly
    Too much code
    Scary to refactor

    View full-size slide

  144. function createOrder(shoppingCart, customer) {
    let total = R.pipe(
    addPrices(shoppingCart.items),
    addDiscounts(shoppingCart.discounts),
    addTax(shoppingCart.tax),
    add(shoppingCart.shippingCost),
    roundTotal,
    )(0)
    return { status: 'new', customer, total }
    }

    View full-size slide

  145. const createOrder = customer => shoppingCart => {
    let total = R.pipe(
    addPrices(shoppingCart.items),
    addDiscounts(shoppingCart.discounts),
    addTax(shoppingCart.tax),
    add(shoppingCart.shippingCost),
    roundTotal,
    )(0)
    return { status: 'new', customer, total }
    }

    View full-size slide

  146. createOrder(myShoppingCart, 'Tucker')

    View full-size slide

  147. Strong Static
    Types

    View full-size slide

  148. createOrder shoppingCart customer =
    let
    total =
    addPrices shoppingCart.items 0
    |> addDiscounts shoppingCart.discounts
    |> addTax shoppingCart.tax
    |> add shoppingCart.shippingCost
    |> roundTotal
    in
    { status = "new"
    , customer = customer
    , total = total
    }

    View full-size slide

  149. createOrder shoppingCart customer =
    let
    total =
    addPrices shoppingCart.items 0
    |> addDiscounts shoppingCart.discounts
    |> addTax shoppingCart.tax
    |> add shoppingCart.shippingCost
    |> roundTotal
    in
    { status = "new"
    , customer = customer
    , total = total
    }

    View full-size slide

  150. createOrder shoppingCart customer =
    let
    total =
    addPrices shoppingCart.items 0
    |> addDiscounts shoppingCart.discounts
    |> addTax shoppingCart.tax
    |> add shoppingCart.shippingCost
    |> roundTotal
    in
    { status = "new"
    , customer = customer
    , total = total
    }

    View full-size slide

  151. createOrder customer shoppingCart =
    let
    total =
    addPrices shoppingCart.items 0
    |> addDiscounts shoppingCart.discounts
    |> addTax shoppingCart.tax
    |> add shoppingCart.shippingCost
    |> roundTotal
    in
    { status = "new"
    , customer = customer
    , total = total
    }

    View full-size slide

  152. -- TYPE MISMATCH ------------------------- App.elm
    The 2nd argument to `createOrder` is not what I expect:
    66| createOrder myShoppingCart "Tucker"
    ^^^^^^^^
    This argument is a string of type:
    String
    But `createOrder` needs the 2nd argument to be:
    { b
    | discounts : List Float
    , items : List { a | price : Float }
    , shippingCost : Float
    , tax : Float
    }

    View full-size slide

  153. -- TYPE MISMATCH ------------------------- App.elm
    The 2nd argument to `createOrder` is not what I expect:
    66| createOrder myShoppingCart "Tucker"
    ^^^^^^^^
    This argument is a string of type:
    String
    But `createOrder` needs the 2nd argument to be:
    { b
    | discounts : List Float
    , items : List { a | price : Float }
    , shippingCost : Float
    , tax : Float
    }

    View full-size slide

  154. -- TYPE MISMATCH ------------------------- App.elm
    The 2nd argument to `createOrder` is not what I expect:
    66| createOrder myShoppingCart "Tucker"
    ^^^^^^^^
    This argument is a string of type:
    String
    But `createOrder` needs the 2nd argument to be:
    { b
    | discounts : List Float
    , items : List { a | price : Float }
    , shippingCost : Float
    , tax : Float
    }

    View full-size slide

  155. conference : String
    conference = "Lambda Squared"
    life : Int
    life = 42
    greet : String -> String
    greet name =
    "Hello, " ++ name
    formatCurrency : Float -> String
    formatCurrency amount =
    "$" ++ toString amount

    View full-size slide

  156. conference : String
    conference = "Lambda Squared"
    life : Int
    life = 42
    greet : String -> String
    greet name =
    "Hello, " ++ name
    formatCurrency : Float -> String
    formatCurrency amount =
    "$" ++ toString amount

    View full-size slide

  157. conference : String
    conference = "Lambda Squared"
    life : Int
    life = 42
    greet : String -> String
    greet name =
    "Hello, " ++ name
    formatCurrency : Float -> String
    formatCurrency amount =
    "$" ++ toString amount

    View full-size slide

  158. type Item = {
    price: number,
    }

    View full-size slide

  159. type Item = {
    price: number,
    }

    View full-size slide

  160. type Item = {
    price: number,
    }

    View full-size slide

  161. type Item = {
    price: number,
    }
    type ShoppingCart = {
    items: Item[],
    discounts: number[],
    tax: number,
    shippingCost: number,
    }

    View full-size slide

  162. type Item = {
    price: number,
    }
    type ShoppingCart = {
    items: Item[],
    discounts: number[],
    tax: number,
    shippingCost: number,
    }

    View full-size slide

  163. type Item = {
    price: number,
    }
    type ShoppingCart = {
    items: Item[],
    discounts: number[],
    tax: number,
    shippingCost: number,
    }
    type Order = {
    status: string,
    customer: string,
    total: number,
    }

    View full-size slide

  164. function createOrder(
    shoppingCart: ShoppingCart,
    customer: string
    ): Order {
    ...
    }

    View full-size slide

  165. function createOrder(
    shoppingCart: ShoppingCart,
    customer: string
    ): Order {
    ...
    }

    View full-size slide

  166. type Order = {
    status: string,
    customer: string,
    total: number,
    }

    View full-size slide

  167. type Order = {
    status: string,
    customer: string,
    total: number,
    }

    View full-size slide

  168. enum Status {
    New,
    Processing,
    Shipped,
    }

    View full-size slide

  169. enum Status {
    New,
    Processing,
    Shipped,
    }

    View full-size slide

  170. enum Status {
    New,
    Processing,
    Shipped,
    }

    View full-size slide

  171. enum Status {
    New,
    Processing,
    Shipped,
    }
    type Order = {
    status: Status,
    customer: string,
    total: number,
    }

    View full-size slide

  172. function createOrder(
    shoppingCart: ShoppingCart,
    customer: string
    ): Order {
    let total = R.pipe(
    addPrices(shoppingCart.items),
    addDiscounts(shoppingCart.discounts),
    addTax(shoppingCart.tax),
    add(shoppingCart.shippingCost),
    roundTotal,
    )(0)
    return { status: Status.New, customer, total }
    }

    View full-size slide

  173. Hard to test Hard to follow
    Code breaks
    unexpectedly
    Too much code
    Scary to refactor
    Oh, null and
    undefined

    View full-size slide

  174. function getBestFriendLocation(id, users) {
    let user = users.get(id)
    let friend = users.get(user.bestFriendId)
    return friend.location
    }

    View full-size slide

  175. function getBestFriendLocation(id, users) {
    let user = users.get(id)
    let friend = users.get(user.bestFriendId)
    return friend.location
    }

    View full-size slide

  176. function getBestFriendLocation(id, users) {
    let user = users.get(id)
    let friend = users.get(user.bestFriendId)
    return friend.location
    }

    View full-size slide

  177. function getBestFriendLocation(id, users) {
    let user = users.get(id)
    let friend = users.get(user.bestFriendId)
    return friend.location
    }

    View full-size slide

  178. getBestFriendLocation(42, users)
    Uncaught TypeError:
    Cannot read property
    'bestFriendId' of null

    View full-size slide

  179. function getBestFriendLocation(id, users) {
    let user = users.get(id)
    let friend = users.get(user.bestFriendId)
    return friend.location
    }
    null

    View full-size slide

  180. function getBestFriendLocation(id, users) {
    let user = users.get(id)
    if (user) {
    let friend = users.get(user.bestFriendId)
    return friend.location
    }
    }

    View full-size slide

  181. function getBestFriendLocation(id, users) {
    let user = users.get(id)
    if (user) {
    let friend = users.get(user.bestFriendId)
    return friend.location
    }
    }
    null

    View full-size slide

  182. function getBestFriendLocation(id, users) {
    let user = users.get(id)
    if (user && user.bestFriendId) {
    let friend = users.get(user.bestFriendId)
    return friend.location
    }
    }

    View full-size slide

  183. function getBestFriendLocation(id, users) {
    let user = users.get(id)
    if (user && user.bestFriendId) {
    let friend = users.get(user.bestFriendId)
    return friend.location
    }
    } null

    View full-size slide

  184. function getBestFriendLocation(id, users) {
    let user = users.get(id)
    if (user && user.bestFriendId) {
    let friend = users.get(user.bestFriendId)
    if (friend) {
    return friend.location
    }
    }
    }

    View full-size slide

  185. if () {}
    if () {}
    if
    ()
    {}

    View full-size slide

  186. type Maybe a
    = Nothing
    | Just a

    View full-size slide

  187. type Maybe a
    = Nothing
    | Just a

    View full-size slide

  188. type Maybe a
    = Nothing
    | Just a

    View full-size slide

  189. type Maybe a
    = Nothing
    | Just a

    View full-size slide

  190. 42
    Just 42
    "Hi"
    Just "Hi"

    View full-size slide

  191. Just 42 [42]
    Nothing [ ]

    View full-size slide

  192. import { Maybe } from 'true-myth'
    Maybe.just(42) // Just 42
    Maybe.nothing() // Nothing
    Maybe.of(42) // Just 42
    Maybe.of(null) // Nothing
    Maybe.of(undefined) // Nothing

    View full-size slide

  193. function mapGet(key, map) {
    let result = map.get(key)
    return result == null
    ? Maybe.nothing()
    : Maybe.just(result)
    }

    View full-size slide

  194. let mapGet = Maybe.wrapReturn(
    (key, map) => map.get(key)
    )

    View full-size slide

  195. function getUserName(id, users) {
    let user = mapGet(id, users)
    if (user.isJust()) {
    return user.unsafelyUnwrap().name
    }
    }

    View full-size slide

  196. function getUserName(id, users) {
    let user = mapGet(id, users)
    if (user.isJust()) {
    return user.unsafelyUnwrap().name
    }
    }
    Maybe User

    View full-size slide

  197. function getUserName(id, users) {
    let user = mapGet(id, users)
    if (user.isJust()) {
    return user.unsafelyUnwrap().name
    }
    }

    View full-size slide

  198. function getUserName(id, users) {
    let user = mapGet(id, users)
    if (user.isJust()) {
    return user.unsafelyUnwrap().name
    }
    }
    user

    View full-size slide

  199. function getUserName(id, users) {
    let user = mapGet(id, users)
    if (user.isJust()) {
    return user.unsafelyUnwrap().name
    }
    }
    Nothing?

    View full-size slide

  200. function getUserName(
    id: Id,
    users: Map
    ): string {
    let user = mapGet(id, users)
    if (user.isJust()) {
    return user.unsafelyUnwrap().name
    }
    }

    View full-size slide

  201. null.ts:27:4 - error TS2366:
    Function lacks ending return statement
    and return type does not include 'undefined'.
    27 ): string {
    ~~~~~~
    Found 1 error.
    $ tsc --lib es2015 --strictNullChecks null.ts

    View full-size slide

  202. function getUserName(
    id: Id,
    users: Map
    ): string {
    let user = mapGet(id, users)
    return user.isJust()
    ? user.unsafelyUnwrap().name
    : ''
    }

    View full-size slide

  203. function getBestFriendLocation(id, users) {
    let user = users.get(id)
    if (user && user.bestFriendId) {
    let friend = users.get(user.bestFriendId)
    if (friend) {
    return friend.location
    }
    }
    }

    View full-size slide

  204. function getBestFriendLocation(
    id: Id,
    users: Map
    ): Maybe {
    let user = mapGet(id, users)
    if (user.isJust()) {
    let { bestFriendId } = user.unsafelyUnwrap()
    if (bestFriendId.isJust()) {
    let friend = mapGet(bestFriendId.unsafelyUnwrap(), users)
    if (friend.isJust()) {
    return friend.unsafelyUnwrap().location
    }
    return Maybe.nothing()
    }
    return Maybe.nothing()
    }
    return Maybe.nothing()
    }

    View full-size slide

  205. function getBestFriendLocation(
    id: Id,
    users: Map
    ): Maybe {
    return mapGet(id, users)
    .chain(user => user.bestFriendId)
    .chain(bestFriendId => mapGet(bestFriendId, users))
    .chain(friend => friend.location)
    }

    View full-size slide

  206. function getBestFriendLocation(
    id: Id,
    users: Map
    ): Maybe {
    return mapGet(id, users)
    .chain(user => user.bestFriendId)
    .chain(bestFriendId => mapGet(bestFriendId, users))
    .chain(friend => friend.location)
    }

    View full-size slide

  207. function getBestFriendLocation(
    id: Id,
    users: Map
    ): Maybe {
    return mapGet(id, users)
    .chain(user => user.bestFriendId)
    .chain(bestFriendId => mapGet(bestFriendId, users))
    .chain(friend => friend.location)
    }

    View full-size slide

  208. function getBestFriendLocation(
    id: Id,
    users: Map
    ): Maybe {
    return mapGet(id, users)
    .chain(user => user.bestFriendId)
    .chain(bestFriendId => mapGet(bestFriendId, users))
    .chain(friend => friend.location)
    }

    View full-size slide

  209. function getBestFriendLocation(
    id: Id,
    users: Map
    ): Maybe {
    return mapGet(id, users)
    .chain(user => user.bestFriendId)
    .chain(bestFriendId => mapGet(bestFriendId, users))
    .chain(friend => friend.location)
    }

    View full-size slide

  210. function getBestFriendLocation(
    id: Id,
    users: Map
    ): Maybe {
    return mapGet(id, users)
    .chain(user => user.bestFriendId)
    .chain(bestFriendId => mapGet(bestFriendId, users))
    .chain(friend => friend.location)
    }

    View full-size slide

  211. function getBestFriendLocation(
    id: Id,
    users: Map
    ): Maybe {
    return mapGet(id, users)
    .chain(user => user.bestFriendId)
    .chain(bestFriendId => mapGet(bestFriendId, users))
    .chain(friend => friend.location)
    }

    View full-size slide

  212. P.S.
    You kinda, sorta just learned monads

    View full-size slide

  213. Hard to test Hard to follow
    Code breaks
    unexpectedly
    Too much code
    Scary to refactor
    Oh, null and
    undefined

    View full-size slide

  214. Hard to test
    Pure Functions
    Hard to follow
    Code breaks
    unexpectedly
    Too much code
    Scary to refactor
    Oh, null and
    undefined

    View full-size slide

  215. Hard to test
    Pure Functions
    Hard to follow
    Declarative Code
    Code breaks
    unexpectedly
    Too much code
    Scary to refactor
    Oh, null and
    undefined

    View full-size slide

  216. Code breaks unexpectedly
    Immutable Data Too much code
    Scary to refactor Oh, null
    Hard to test
    Pure Functions
    Hard to follow
    Declarative Code

    View full-size slide

  217. Too much code
    Curried, Composable
    Functions
    Scary to refactor
    Code breaks unexpectedly
    Immutable Data
    Hard to test
    Pure Functions
    Hard to follow
    Declarative Code
    Oh, null and
    undefined

    View full-size slide

  218. Scary to refactor
    Strong, Static Types
    Too much code
    Curried, Composable
    Functions
    Code breaks unexpectedly
    Immutable Data
    Hard to test
    Pure Functions
    Hard to follow
    Declarative Code
    Oh, null and
    undefined

    View full-size slide

  219. Oh, null and undefined
    Maybe and Monads
    Scary to refactor
    Strong, Static Types
    Too much code
    Curried, Composable
    Functions
    Code breaks unexpectedly
    Immutable Data
    Hard to test
    Pure Functions
    Hard to follow
    Declarative Code

    View full-size slide

  220. Thanks!
    bit.ly/ct-practical-fp
    Jeremy Fairbank
    @elpapapollo

    View full-size slide