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

All Things Open 2018: Practical Functional Programming

All Things Open 2018: 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 22, 2018
Tweet

More Decks by Jeremy Fairbank

Other Decks in Programming

Transcript

  1. 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) } }
  2. 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. 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. 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. 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. 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 })
  7. 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. 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. 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. 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')) }) })
  11. 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. 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. 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. 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) } }
  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) } }
  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) } }
  17. const add = (x, y) => x + y add(2,

    3) === 5 add(2, 3) === 5 add(2, 3) === 5
  18. const url = ids => `/orders?ids=${ids.join(',')}` const orderDetails = ({

    id, date, customer }) => `Order ${id} on ${date} by ${customer}` const detailsForOrders = orders => orders.map(orderDetails)
  19. const url = ids => `/orders?ids=${ids.join(',')}` const orderDetails = ({

    id, date, customer }) => `Order ${id} on ${date} by ${customer}` const detailsForOrders = orders => orders.map(orderDetails)
  20. const url = ids => `/orders?ids=${ids.join(',')}` const orderDetails = ({

    id, date, customer }) => `Order ${id} on ${date} by ${customer}` const detailsForOrders = orders => orders.map(orderDetails)
  21. const url = ids => `/orders?ids=${ids.join(',')}` const orderDetails = ({

    id, date, customer }) => `Order ${id} on ${date} by ${customer}` const detailsForOrders = orders => orders.map(orderDetails)
  22. const url = ids => `/orders?ids=${ids.join(',')}` const orderDetails = ({

    id, date, customer }) => `Order ${id} on ${date} by ${customer}` const detailsForOrders = orders => orders.map(orderDetails)
  23. { id: 1, date: '12/1/17', customer: 'Tucker' } { id:

    2, date: '11/25/17', customer: 'Sally' } { id: 3, date: '3/30/18', customer: 'Joe' }
  24. { 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
  25. [ '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
  26. 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' }
  27. 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', ]) })
  28. async function printOrderDetails(ids) { let orders = await fetchJson(url(ids)) let

    details = detailsForOrders(orders) details.forEach(orderDetails => { console.log(orderDetails) }) }
  29. 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}` } }
  30. 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}` } }
  31. 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}` } }
  32. 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}` } }
  33. 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
  34. 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
  35. 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
  36. 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
  37. class Person { // ... describe() { // ... let

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

    hobbies = this.hobbies.splice(0, 2).join(' and ') // ... } } TypeError: Cannot add/remove sealed array elements
  39. class Person { // ... describe() { // ... let

    hobbies = this.hobbies.slice(0, 2).join(' and ') // ... } }
  40. 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}` }
  41. 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}` }
  42. 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}` }
  43. let person = create('Jeremy', []) person = addHobby('programming', person) person

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

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

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

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

    = addHobby('reading', person) describe(person) // Jeremy likes to do things such as programming and reading
  48. const greet = (greeting, name) => `${greeting}, ${name}` // ..

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

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

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

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

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

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

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

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

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

    greetInEnglish = greet('Hi') const greetInSpanish = greet('Hola') Curried Function Partial Application
  58. greet greeting name = greeting ++ ", " ++ name

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

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

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

    greetInEnglish = greet "Hi" greetInSpanish = greet "Hola"
  62. 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 } }
  63. 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 } }
  64. 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 } }
  65. 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 } }
  66. 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 } }
  67. 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 } }
  68. 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 } }
  69. 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 } }
  70. const toUpper = string => string.toUpperCase() const greet = greeting

    => name => `${greeting}, ${name}` const exclaim = phrase => `${phrase}!` const excitedGreeting = name => exclaim(greet('Hi')(toUpper(name))) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  71. const toUpper = string => string.toUpperCase() const greet = greeting

    => name => `${greeting}, ${name}` const exclaim = phrase => `${phrase}!` const excitedGreeting = name => exclaim(greet('Hi')(toUpper(name))) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  72. const toUpper = string => string.toUpperCase() const greet = greeting

    => name => `${greeting}, ${name}` const exclaim = phrase => `${phrase}!` const excitedGreeting = name => exclaim(greet('Hi')(toUpper(name))) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  73. const toUpper = string => string.toUpperCase() const greet = greeting

    => name => `${greeting}, ${name}` const exclaim = phrase => `${phrase}!` const excitedGreeting = name => exclaim(greet('Hi')(toUpper(name))) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  74. const toUpper = string => string.toUpperCase() const greet = greeting

    => name => `${greeting}, ${name}` const exclaim = phrase => `${phrase}!` const excitedGreeting = name => exclaim(greet('Hi')(toUpper(name))) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  75. const toUpper = string => string.toUpperCase() const greet = greeting

    => name => `${greeting}, ${name}` const exclaim = phrase => `${phrase}!` const excitedGreeting = name => exclaim(greet('Hi')(toUpper(name))) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  76. const toUpper = string => string.toUpperCase() const greet = greeting

    => name => `${greeting}, ${name}` const exclaim = phrase => `${phrase}!` const excitedGreeting = name => exclaim(greet('Hi')(toUpper(name))) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  77. const toUpper = string => string.toUpperCase() const greet = greeting

    => name => `${greeting}, ${name}` const exclaim = phrase => `${phrase}!` const excitedGreeting = name => exclaim(greet('Hi')(toUpper(name))) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  78. const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), exclaim, )(name) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  79. const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), exclaim, )(name) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  80. const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), exclaim, )(name) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  81. const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), exclaim, )(name) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  82. const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, // ALL THINGS OPEN greet('Hi'), exclaim, )(name) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  83. const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), // Hi, ALL THINGS OPEN exclaim, )(name) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  84. const R = require('ramda') const excitedGreeting = name => R.pipe(

    toUpper, greet('Hi'), exclaim, // Hi, ALL THINGS OPEN! )(name) excitedGreeting('All Things Open') // Hi, ALL THINGS OPEN!
  85. greet greeting name = greeting ++ ", " ++ name

    exclaim phrase = phrase ++ "!" excitedGreeting name = name |> String.toUpper |> greet "Hi" |> exclaim excitedGreeting "All Things Open" -- Hi, ALL THINGS OPEN!
  86. greet greeting name = greeting ++ ", " ++ name

    exclaim phrase = phrase ++ "!" excitedGreeting name = name |> String.toUpper |> greet "Hi" |> exclaim excitedGreeting "All Things Open" -- Hi, ALL THINGS OPEN!
  87. greet greeting name = greeting ++ ", " ++ name

    exclaim phrase = phrase ++ "!" excitedGreeting name = name |> String.toUpper |> greet "Hi" |> exclaim excitedGreeting "All Things Open" -- Hi, ALL THINGS OPEN!
  88. greet greeting name = greeting ++ ", " ++ name

    exclaim phrase = phrase ++ "!" excitedGreeting name = name |> String.toUpper |> greet "Hi" |> exclaim excitedGreeting "All Things Open" -- Hi, ALL THINGS OPEN!
  89. greet greeting name = greeting ++ ", " ++ name

    exclaim phrase = phrase ++ "!" excitedGreeting name = name |> String.toUpper -- ALL THINGS OPEN |> greet "Hi" |> exclaim excitedGreeting "All Things Open" -- Hi, ALL THINGS OPEN!
  90. greet greeting name = greeting ++ ", " ++ name

    exclaim phrase = phrase ++ "!" excitedGreeting name = name |> String.toUpper |> greet "Hi" -- Hi, ALL THINGS OPEN |> exclaim excitedGreeting "All Things Open" -- Hi, ALL THINGS OPEN!
  91. greet greeting name = greeting ++ ", " ++ name

    exclaim phrase = phrase ++ "!" excitedGreeting name = name |> String.toUpper |> greet "Hi" |> exclaim -- Hi, ALL THINGS OPEN! excitedGreeting "All Things Open" -- Hi, ALL THINGS OPEN!
  92. 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 } }
  93. 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 } }
  94. 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 } }
  95. 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 } }
  96. 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 } }
  97. 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 } }
  98. 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 } }
  99. 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 }
  100. 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 }
  101. 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 }
  102. 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 }
  103. -- TYPE MISMATCH ------------------------- Responsibilities.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 }
  104. -- TYPE MISMATCH ------------------------- Responsibilities.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 }
  105. -- TYPE MISMATCH ------------------------- Responsibilities.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 }
  106. conference : String conference = "Lambda Squared" life : Int

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

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

    life = 42 greet : String -> String greet name = "Hello, " ++ name formatCurrency : Float -> String formatCurrency amount = "$" ++ String.fromFloat amount
  109. type alias Item = { price : Float } type

    alias ShoppingCart = { items : List Item , discounts : List Float , tax : Float , shippingCost : Float }
  110. type alias Item = { price : Float } type

    alias ShoppingCart = { items : List Item , discounts : List Float , tax : Float , shippingCost : Float }
  111. type alias Item = { price : Float } type

    alias ShoppingCart = { items : List Item , discounts : List Float , tax : Float , shippingCost : Float } type alias Order = { status : String , customer : String , total : Float }
  112. type Status = New | Processing | Shipped type alias

    Order = { status : Status , customer : String , total : Float }
  113. createOrder : ShoppingCart -> String -> Order 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 }
  114. function getBestFriendLocation(id, users) { let user = users.get(id) let friend

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

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

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

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

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

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

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

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

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

    && user.bestFriendId) { let friend = users.get(user.bestFriendId) if (friend) { return friend.location } } }
  124. getUserName : Id -> Dict Id User -> String getUserName

    id users = Dict.get id users Maybe User
  125. getUserName : Id -> Dict Id User -> String getUserName

    id users = case Dict.get id users of Just user -> user.name
  126. getUserName : Id -> Dict Id User -> String getUserName

    id users = case Dict.get id users of Just user -> user.name
  127. getUserName : Id -> Dict Id User -> String getUserName

    id users = case Dict.get id users of Just user -> user.name user
  128. getUserName : Id -> Dict Id User -> String getUserName

    id users = case Dict.get id users of Just user -> user.name
  129. -- MISSING PATTERNS ------------------------------ App.elm This `case` does not have

    branches for all possibilities: 31|> case Dict.get id users of 32|> Just user -> 33|> user.name Missing possibilities include: Nothing I would have to crash if I saw one of those. Add branches for them!
  130. getUserName : Id -> Dict Id User -> String getUserName

    id users = case Dict.get id users of Just user -> user.name Nothing -> "<anonymous>"
  131. function getBestFriendLocation(id, users) { let user = users.get(id) if (user

    && user.bestFriendId) { let friend = users.get(user.bestFriendId) if (friend) { return friend.location } } }
  132. getBestFriendLocation : Id -> Dict Id User -> Maybe Location

    getBestFriendLocation id users = case Dict.get id users of Just user -> case user.bestFriendId of Just bestFriendId -> case Dict.get bestFriendId users of Just friend -> friend.location Nothing -> Nothing Nothing -> Nothing Nothing -> Nothing
  133. getBestFriendLocation id users = Dict.get id users |> Maybe.andThen (\user

    -> user.bestFriendId) |> Maybe.andThen (\fid -> Dict.get fid users) |> Maybe.andThen (\friend -> friend.location)
  134. getBestFriendLocation id users = Dict.get id users |> Maybe.andThen (\user

    -> user.bestFriendId) |> Maybe.andThen (\fid -> Dict.get fid users) |> Maybe.andThen (\friend -> friend.location)
  135. getBestFriendLocation id users = Dict.get id users |> Maybe.andThen (\user

    -> user.bestFriendId) |> Maybe.andThen (\fid -> Dict.get fid users) |> Maybe.andThen (\friend -> friend.location)
  136. getBestFriendLocation id users = Dict.get id users |> Maybe.andThen (\user

    -> user.bestFriendId) |> Maybe.andThen (\fid -> Dict.get fid users) |> Maybe.andThen (\friend -> friend.location)
  137. getBestFriendLocation id users = Dict.get id users |> Maybe.andThen (\user

    -> user.bestFriendId) |> Maybe.andThen (\fid -> Dict.get fid users) |> Maybe.andThen (\friend -> friend.location)
  138. getBestFriendLocation id users = Dict.get id users |> Maybe.andThen (\user

    -> user.bestFriendId) |> Maybe.andThen (\fid -> Dict.get fid users) |> Maybe.andThen (\friend -> friend.location)
  139. getBestFriendLocation id users = Dict.get id users |> Maybe.andThen (\user

    -> user.bestFriendId) |> Maybe.andThen (\fid -> Dict.get fid users) |> Maybe.andThen (\friend -> friend.location)
  140. getBestFriendLocation id users = Dict.get id users |> Maybe.andThen (\user

    -> user.bestFriendId) |> Maybe.andThen (\fid -> Dict.get fid users) |> Maybe.andThen (\friend -> friend.location)
  141. Code breaks unexpectedly Immutable Data Too much code Scary to

    refactor Oh, null Hard to test Pure Functions
  142. Too much code Curried, Composable Functions Scary to refactor Oh,

    null Code breaks unexpectedly Immutable Data Hard to test Pure Functions
  143. Scary to refactor Strong, Static Types Oh, null Too much

    code Curried, Composable Functions Code breaks unexpectedly Immutable Data Hard to test Pure Functions
  144. Oh, null 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