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.

94bd558238b69c45d3d3e15797ae94f7?s=128

Jeremy Fairbank

October 18, 2019
Tweet

Transcript

  1. 12.
  2. 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) } }
  3. 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) } }
  4. 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) } }
  5. 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) } }
  6. 19.

    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) } }
  7. 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 })
  8. 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 })
  9. 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 })
  10. 23.

    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 })
  11. 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')) }) })
  12. 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')) }) })
  13. 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')) }) })
  14. 27.

    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')) }) })
  15. 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) } }
  16. 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) } }
  17. 31.

    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) } }
  18. 34.
  19. 35.

    const add = (x, y) => x + y add(2,

    3) === 5 add(2, 3) === 5 add(2, 3) === 5
  20. 36.
  21. 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 }
  22. 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 }
  23. 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 }
  24. 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 }
  25. 41.

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

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

    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 }
  28. 45.

    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', ]) })
  29. 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]) } }
  30. 47.

    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]) } }
  31. 48.

    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]) } }
  32. 49.

    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]) } }
  33. 52.

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

    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 }
  35. 56.

    const orderDetails = ({ id, date, customer }) => `Order

    ${id} on ${date} by ${customer}` const detailsForOrders = orders => orders.map(orderDetails)
  36. 57.

    const orderDetails = ({ id, date, customer }) => `Order

    ${id} on ${date} by ${customer}` const detailsForOrders = orders => orders.map(orderDetails)
  37. 58.

    { id: 1, date: '12/1/17', customer: 'Tucker' } { id:

    2, date: '11/25/17', customer: 'Sally' } { id: 3, date: '3/30/18', customer: 'Joe' }
  38. 59.

    { 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
  39. 60.

    [ '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
  40. 61.

    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' }
  41. 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}` } }
  42. 64.

    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}` } }
  43. 65.

    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}` } }
  44. 66.

    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}` } }
  45. 67.

    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}` } }
  46. 68.

    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}` } }
  47. 69.

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

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

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

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

    class Person { // ... describe() { // ... let

    hobbies = this.hobbies.splice(0, 2).join(' and ') // ... } }
  52. 78.

    class Person { // ... describe() { // ... let

    hobbies = this.hobbies.splice(0, 2).join(' and ') // ... } } TypeError
  53. 79.

    class Person { // ... describe() { // ... let

    hobbies = this.hobbies.slice(0, 2).join(' and ') // ... } }
  54. 81.

    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}` }
  55. 82.

    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}` }
  56. 83.

    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}` }
  57. 89.

    let person = create('Jeremy', []) person = addHobby('programming', person) person

    = addHobby('reading', person) describe(person) // Jeremy likes to do things such as programming and reading
  58. 90.

    let person = create('Jeremy', []) person = addHobby('programming', person) person

    = addHobby('reading', person) describe(person) // Jeremy likes to do things such as programming and reading
  59. 91.

    let person = create('Jeremy', []) person = addHobby('programming', person) person

    = addHobby('reading', person) describe(person) // Jeremy likes to do things such as programming and reading
  60. 92.

    let person = create('Jeremy', []) person = addHobby('programming', person) person

    = addHobby('reading', person) describe(person) // Jeremy likes to do things such as programming and reading
  61. 93.

    let person = create('Jeremy', []) person = addHobby('programming', person) person

    = addHobby('reading', person) describe(person) // Jeremy likes to do things such as programming and reading
  62. 96.

    const greet = (greeting, name) => `${greeting}, ${name}` // ..

    const greetInEnglish = name => greet('Hi', name) const greetInSpanish = name => greet('Hola', name)
  63. 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
  64. 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
  65. 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
  66. 100.

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

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

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

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

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

    const greet = greeting => name => `${greeting}, ${name}` const

    greetInEnglish = greet('Hi') const greetInSpanish = greet('Hola') Curried Function Partial Application
  72. 106.

    greet greeting name = greeting ++ ", " ++ name

    greetInEnglish = greet "Hi" greetInSpanish = greet "Hola"
  73. 107.

    greet greeting name = greeting ++ ", " ++ name

    greetInEnglish = greet "Hi" greetInSpanish = greet "Hola"
  74. 108.

    greet greeting name = greeting ++ ", " ++ name

    greetInEnglish = greet "Hi" greetInSpanish = greet "Hola"
  75. 109.

    greet greeting name = greeting ++ ", " ++ name

    greetInEnglish = greet "Hi" greetInSpanish = greet "Hola"
  76. 111.
  77. 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 } }
  78. 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 } }
  79. 114.

    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 } }
  80. 115.

    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 } }
  81. 116.

    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 } }
  82. 117.

    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 } }
  83. 118.

    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 } }
  84. 119.

    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 } }
  85. 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!
  86. 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!
  87. 123.

    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!
  88. 124.

    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!
  89. 125.

    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!
  90. 126.

    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!
  91. 127.

    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!
  92. 128.

    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!
  93. 129.

    const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), exclaim, )(name) excitedGreeting('Connect.Tech') // Hi, CONNECT.TECH!
  94. 130.

    const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), exclaim, )(name) excitedGreeting('Connect.Tech') // Hi, CONNECT.TECH!
  95. 131.

    const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), exclaim, )(name) excitedGreeting('Connect.Tech') // Hi, CONNECT.TECH!
  96. 132.

    const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), exclaim, )(name) excitedGreeting('Connect.Tech') // Hi, CONNECT.TECH!
  97. 133.

    const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, // CONNECT.TECH greet('Hi'), exclaim, )(name) excitedGreeting('Connect.Tech') // Hi, CONNECT.TECH!
  98. 134.

    const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), // Hi, CONNECT.TECH exclaim, )(name) excitedGreeting('Connect.Tech') // Hi, CONNECT.TECH!
  99. 135.

    const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), exclaim, // Hi, CONNECT.TECH! )(name) excitedGreeting('Connect.Tech') // Hi, CONNECT.TECH!
  100. 137.

    greet greeting name = greeting ++ ", " ++ name

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

    greet greeting name = greeting ++ ", " ++ name

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

    greet greeting name = greeting ++ ", " ++ name

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

    greet greeting name = greeting ++ ", " ++ name

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

    greet greeting name = greeting ++ ", " ++ name

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

    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!
  106. 143.

    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!
  107. 144.

    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 } }
  108. 145.

    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 } }
  109. 146.

    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 } }
  110. 147.

    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 } }
  111. 148.

    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 } }
  112. 150.

    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 } }
  113. 151.

    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 } }
  114. 154.

    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 }
  115. 155.

    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 }
  116. 156.

    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 }
  117. 157.

    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 }
  118. 158.

    -- 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 }
  119. 159.

    -- 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 }
  120. 160.

    -- 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 }
  121. 161.

    conference : String conference = "Lambda Squared" life : Int

    life = 42 greet : String -> String greet name = "Hello, " ++ name formatCurrency : Float -> String formatCurrency amount = "$" ++ toString amount
  122. 162.

    conference : String conference = "Lambda Squared" life : Int

    life = 42 greet : String -> String greet name = "Hello, " ++ name formatCurrency : Float -> String formatCurrency amount = "$" ++ toString amount
  123. 163.

    conference : String conference = "Lambda Squared" life : Int

    life = 42 greet : String -> String greet name = "Hello, " ++ name formatCurrency : Float -> String formatCurrency amount = "$" ++ toString amount
  124. 164.

    ?

  125. 165.
  126. 170.

    type Item = { price: number, } type ShoppingCart =

    { items: Item[], discounts: number[], tax: number, shippingCost: number, }
  127. 171.

    type Item = { price: number, } type ShoppingCart =

    { items: Item[], discounts: number[], tax: number, shippingCost: number, }
  128. 172.

    type Item = { price: number, } type ShoppingCart =

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

    enum Status { New, Processing, Shipped, } type Order =

    { status: Status, customer: string, total: number, }
  130. 181.

    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 } }
  131. 182.

    Hard to test Hard to follow Code breaks unexpectedly Too

    much code Scary to refactor Oh, null and undefined
  132. 183.

    function getBestFriendLocation(id, users) { let user = users.get(id) let friend

    = users.get(user.bestFriendId) return friend.location }
  133. 184.

    function getBestFriendLocation(id, users) { let user = users.get(id) let friend

    = users.get(user.bestFriendId) return friend.location }
  134. 185.

    function getBestFriendLocation(id, users) { let user = users.get(id) let friend

    = users.get(user.bestFriendId) return friend.location }
  135. 186.

    function getBestFriendLocation(id, users) { let user = users.get(id) let friend

    = users.get(user.bestFriendId) return friend.location }
  136. 188.

    function getBestFriendLocation(id, users) { let user = users.get(id) let friend

    = users.get(user.bestFriendId) return friend.location } null
  137. 189.

    function getBestFriendLocation(id, users) { let user = users.get(id) if (user)

    { let friend = users.get(user.bestFriendId) return friend.location } }
  138. 190.

    function getBestFriendLocation(id, users) { let user = users.get(id) if (user)

    { let friend = users.get(user.bestFriendId) return friend.location } } null
  139. 191.

    function getBestFriendLocation(id, users) { let user = users.get(id) if (user

    && user.bestFriendId) { let friend = users.get(user.bestFriendId) return friend.location } }
  140. 192.

    function getBestFriendLocation(id, users) { let user = users.get(id) if (user

    && user.bestFriendId) { let friend = users.get(user.bestFriendId) return friend.location } } null
  141. 193.

    function getBestFriendLocation(id, users) { let user = users.get(id) if (user

    && user.bestFriendId) { let friend = users.get(user.bestFriendId) if (friend) { return friend.location } } }
  142. 194.
  143. 202.

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

    function mapGet(key, map) { let result = map.get(key) return result

    == null ? Maybe.nothing() : Maybe.just(result) }
  145. 205.

    function getUserName(id, users) { let user = mapGet(id, users) if

    (user.isJust()) { return user.unsafelyUnwrap().name } }
  146. 206.

    function getUserName(id, users) { let user = mapGet(id, users) if

    (user.isJust()) { return user.unsafelyUnwrap().name } } Maybe User
  147. 207.

    function getUserName(id, users) { let user = mapGet(id, users) if

    (user.isJust()) { return user.unsafelyUnwrap().name } }
  148. 208.

    function getUserName(id, users) { let user = mapGet(id, users) if

    (user.isJust()) { return user.unsafelyUnwrap().name } } user
  149. 209.

    function getUserName(id, users) { let user = mapGet(id, users) if

    (user.isJust()) { return user.unsafelyUnwrap().name } } Nothing?
  150. 210.

    function getUserName( id: Id, users: Map<Id, User> ): string {

    let user = mapGet(id, users) if (user.isJust()) { return user.unsafelyUnwrap().name } }
  151. 211.

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

    function getUserName( id: Id, users: Map<Id, User> ): string {

    let user = mapGet(id, users) return user.isJust() ? user.unsafelyUnwrap().name : '<anonymous>' }
  153. 213.

    function getBestFriendLocation(id, users) { let user = users.get(id) if (user

    && user.bestFriendId) { let friend = users.get(user.bestFriendId) if (friend) { return friend.location } } }
  154. 214.

    function getBestFriendLocation( id: Id, users: Map<Id, User> ): Maybe<Location> {

    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() }
  155. 215.

    function getBestFriendLocation( id: Id, users: Map<Id, User> ): Maybe<Location> {

    return mapGet(id, users) .chain(user => user.bestFriendId) .chain(bestFriendId => mapGet(bestFriendId, users)) .chain(friend => friend.location) }
  156. 216.

    function getBestFriendLocation( id: Id, users: Map<Id, User> ): Maybe<Location> {

    return mapGet(id, users) .chain(user => user.bestFriendId) .chain(bestFriendId => mapGet(bestFriendId, users)) .chain(friend => friend.location) }
  157. 217.

    function getBestFriendLocation( id: Id, users: Map<Id, User> ): Maybe<Location> {

    return mapGet(id, users) .chain(user => user.bestFriendId) .chain(bestFriendId => mapGet(bestFriendId, users)) .chain(friend => friend.location) }
  158. 218.

    function getBestFriendLocation( id: Id, users: Map<Id, User> ): Maybe<Location> {

    return mapGet(id, users) .chain(user => user.bestFriendId) .chain(bestFriendId => mapGet(bestFriendId, users)) .chain(friend => friend.location) }
  159. 219.

    function getBestFriendLocation( id: Id, users: Map<Id, User> ): Maybe<Location> {

    return mapGet(id, users) .chain(user => user.bestFriendId) .chain(bestFriendId => mapGet(bestFriendId, users)) .chain(friend => friend.location) }
  160. 220.

    function getBestFriendLocation( id: Id, users: Map<Id, User> ): Maybe<Location> {

    return mapGet(id, users) .chain(user => user.bestFriendId) .chain(bestFriendId => mapGet(bestFriendId, users)) .chain(friend => friend.location) }
  161. 221.

    function getBestFriendLocation( id: Id, users: Map<Id, User> ): Maybe<Location> {

    return mapGet(id, users) .chain(user => user.bestFriendId) .chain(bestFriendId => mapGet(bestFriendId, users)) .chain(friend => friend.location) }
  162. 223.

    Hard to test Hard to follow Code breaks unexpectedly Too

    much code Scary to refactor Oh, null and undefined
  163. 224.

    Hard to test Pure Functions Hard to follow Code breaks

    unexpectedly Too much code Scary to refactor Oh, null and undefined
  164. 225.

    Hard to test Pure Functions Hard to follow Declarative Code

    Code breaks unexpectedly Too much code Scary to refactor Oh, null and undefined
  165. 226.

    Code breaks unexpectedly Immutable Data Too much code Scary to

    refactor Oh, null Hard to test Pure Functions Hard to follow Declarative Code
  166. 227.

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

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

    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