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

Curing the Common Loop

Curing the Common Loop

Trying to understand a complex function full of nested loops and conditionals is like trying to read a "choose your own adventure" book from front to back.

In this whirlwind tour of programming with collection pipelines, you'll learn how to use functional programming ideas to break down complex code and abstract hidden duplication behind expressive higher order functions.

Together we'll refactor ugly, complicated spaghetti into a series of simple, elegant transformations; free of loops, complex conditionals, and temporary variables.

Adam Wathan

July 29, 2016
Tweet

More Decks by Adam Wathan

Other Decks in Programming

Transcript

  1. Curing the
    Common Loop
    @adamwathan

    View Slide

  2. $totalRevenue = 0;
    $minSales = 75000;
    foreach ($employees as $employee) {
    $employeeType = $employee->getType();
    if ($employeeType == 'Sales') {
    $customers = $employee->getCustomers();
    foreach ($customers as $customer) {
    $customerTotalSales = 0;
    foreach ($customer->getOrders() as $order) {
    $customerTotalSales += $order->total;
    }
    if ($customerTotalSales >= $minSales) {
    $totalRevenue += $customerTotalSales;
    }
    }
    }
    }
    return $totalRevenue;

    View Slide

  3. Higher Order
    FUNCTIONS

    View Slide

  4. Route::get('/', function () {
    return view('welcome');
    });

    View Slide

  5. Mail::send('order-shipped', $data, function ($m) {
    $m->to($this->customer->email);
    $m->subject("Your order has shipped!");
    });

    View Slide

  6. DB::transaction(function () use ($user) {
    $user->subscription()->delete();
    $user->update([
    'cancelled_at' => Carbon::now()
    ]);
    });

    View Slide

  7. Afford more powerful
    ABSTRACTIONS

    View Slide

  8. For example…

    View Slide

  9. $customerEmails = [];
    foreach ($customers as $customer) {
    $customerEmails[] = $customer->email;
    }
    return $customerEmails;

    View Slide

  10. $stockTotals = [];
    foreach ($inventoryItems as $item) {
    $stockTotals[] = [
    'product' => $item->productName,
    'total_value' => $item->quantity * $item->price,
    ];
    }
    return $stockTotals;

    View Slide

  11. $customerEmails = [];
    foreach ($customers as $customer) {
    $customerEmails[] = $customer->email;
    }
    return $customerEmails;

    View Slide

  12. $customerEmails = [];
    foreach ($customers as $customer) {
    $email = $customer->email;
    $customerEmails[] = $email;
    }
    return $customerEmails;

    View Slide

  13. $stockTotals = [];
    foreach ($inventoryItems as $item) {
    $stockTotals[] = [
    'product' => $item->productName,
    'total_value' => $item->quantity * $item->price,
    ];
    }
    return $stockTotals;

    View Slide

  14. $stockTotals = [];
    foreach ($inventoryItems as $item) {
    $stockTotal = [
    'product' => $item->productName,
    'total_value' => $item->quantity * $item->price,
    ];
    $stockTotals[] = $stockTotal;
    }
    return $stockTotals;

    View Slide

  15. $stockTotals = [];
    foreach ($inventoryItems as $item) {
    $stockTotal = [
    'product' => $item->productName,
    'total_value' => $item->quantity * $item->price,
    ];
    $stockTotals[] = $stockTotal;
    }
    return $stockTotals;

    View Slide

  16. $results = [];
    foreach ($items as $item) {
    $result = [
    'product' => $item->productName,
    'total_value' => $item->quantity * $item->price,
    ];
    $results[] = $result;
    }
    return $results;

    View Slide

  17. $results = [];
    foreach ($items as $item) {
    $result = $item->email;
    $results[] = $result;
    }
    return $results;

    View Slide

  18. $results = [];
    foreach ($items as $item) {
    $result = $item->email;
    $results[] = $result;
    }
    return $results;

    View Slide

  19. $callback = function ($item) {
    return $item->email;
    };
    $results = [];
    foreach ($items as $item) {
    $result = $callback($item);
    $results[] = $result;
    }
    return $results;

    View Slide

  20. $results = [];
    foreach ($items as $item) {
    $result = [
    'product' => $item->productName,
    'total_value' => $item->quantity * $item->price,
    ];
    $results[] = $result;
    }
    return $results;

    View Slide

  21. $callback = function ($item) {
    return [
    'product' => $item->productName,
    'total_value' => $item->quantity * $item->price,
    ];
    };
    $results = [];
    foreach ($items as $item) {
    $result = $callback($item);
    $results[] = $result;
    }
    return $results;

    View Slide

  22. $callback = function ($item) {
    return [
    'product' => $item->productName,
    'total_value' => $item->quantity * $item->price,
    ];
    };
    $results = [];
    foreach ($items as $item) {
    $result = $callback($item);
    $results[] = $result;
    }
    return $results;

    View Slide

  23. $callback = function ($item) {
    return $item->email;
    };
    $results = [];
    foreach ($items as $item) {
    $result = $callback($item);
    $results[] = $result;
    }
    return $results;

    View Slide

  24. function array_map($callback, $items) {
    }
    $results = [];
    foreach ($items as $item) {
    $result = $callback($item);
    $results[] = $result;
    }
    return $results;

    View Slide

  25. $customerEmails = [];
    foreach ($customers as $customer) {
    $customerEmails[] = $customer->email;
    }
    return $customerEmails;

    View Slide

  26. $customerEmails = array_map(function ($customer) {
    return $customer->email;
    }, $customers);

    View Slide

  27. $stockTotals = [];
    foreach ($inventoryItems as $item) {
    $stockTotals[] = [
    'product' => $item->productName,
    'total_value' => $item->quantity * $item->price,
    ];
    }
    return $stockTotals;

    View Slide

  28. $stockTotals = [];
    foreach ($inventoryItems as $item) {
    $stockTotals[] = [
    'product' => $item->productName,
    'total_value' => $item->quantity * $item->price,
    ];
    }
    return $stockTotals;

    View Slide

  29. $stockTotals = array_map(function ($inventoryItem) {
    return [
    'product' => $item->productName,
    'total_value' => $item->quantity * $item->price,
    ];
    }, $inventoryItems);

    View Slide

  30. Another…

    View Slide

  31. $outOfStockProducts = [];
    foreach ($products as $product) {
    if ($product->isOutOfStock()) {
    $outOfStockProducts[] = $product;
    }
    }
    return $outOfStockProducts;

    View Slide

  32. $expensiveProducts = [];
    foreach ($products as $product) {
    if ($product->price > 100) {
    $expensiveProducts[] = $product;
    }
    }
    return $expensiveProducts;

    View Slide

  33. $results = [];
    foreach ($items as $item) {
    if ($item->price > 100) {
    $results[] = $item;
    }
    }
    return $results;

    View Slide

  34. $outOfStockProducts = [];
    foreach ($products as $product) {
    if ($product->isOutOfStock()) {
    $outOfStockProducts[] = $product;
    }
    }
    return $outOfStockProducts;

    View Slide

  35. $results = [];
    foreach ($items as $item) {
    if ($item->isOutOfStock()) {
    $results[] = $item;
    }
    }
    return $results;

    View Slide

  36. $results = [];
    foreach ($items as $item) {
    if ($item->isOutOfStock()) {
    $results[] = $item;
    }
    }
    return $results;

    View Slide

  37. $results = [];
    foreach ($items as $item) {
    if ($item->price > 100) {
    $results[] = $item;
    }
    }
    return $results;

    View Slide

  38. $callback = function ($item) {
    return $item->price > 100;
    };
    $results = [];
    foreach ($items as $item) {
    if ($callback($item)) {
    $results[] = $item;
    }
    }
    return $results;

    View Slide

  39. $results = [];
    foreach ($items as $item) {
    if ($item->isOutOfStock()) {
    $results[] = $item;
    }
    }
    return $results;

    View Slide

  40. $callback = function ($item) {
    return $item->isOutOfStock();
    };
    $results = [];
    foreach ($items as $item) {
    if ($callback($item)) {
    $results[] = $item;
    }
    }
    return $results;

    View Slide

  41. $callback = function ($item) {
    return $item->isOutOfStock();
    };
    $results = [];
    foreach ($items as $item) {
    if ($callback($item)) {
    $results[] = $item;
    }
    }
    return $results;

    View Slide

  42. $callback = function ($item) {
    return $item->price > 100;
    };
    $results = [];
    foreach ($items as $item) {
    if ($callback($item)) {
    $results[] = $item;
    }
    }
    return $results;

    View Slide

  43. function array_filter($items, $callback) {
    $results = [];
    foreach ($items as $item) {
    if ($callback($item)) {
    $results[] = $item;
    }
    }
    return $results;
    }
    $results = [];
    foreach ($items as $item) {
    if ($callback($item)) {
    $results[] = $item;
    }
    }
    return $results;

    View Slide

  44. $outOfStockProducts = [];
    foreach ($products as $product) {
    if ($product->isOutOfStock()) {
    $outOfStockProducts[] = $product;
    }
    }
    return $outOfStockProducts;

    View Slide

  45. $outOfStockProducts = array_filter($products, function ($product) {
    return $product->isOutOfStock();
    });

    View Slide

  46. $expensiveProducts = [];
    foreach ($products as $product) {
    if ($product->price > 100) {
    $expensiveProducts[] = $product;
    }
    }
    return $expensiveProducts;

    View Slide

  47. $expensiveProducts = array_filter($products, function ($product) {
    return $product->price > 100;
    });

    View Slide

  48. function getSalesEmails($employees) {
    $emails = [];
    foreach ($employees as $employee) {
    if ($employee->department === 'Sales') {
    $emails[] = $employee->email;
    }
    }
    return $emails;
    }

    View Slide

  49. function getSalesEmails($employees) {
    $emails = [];
    foreach ($employees as $employee) {
    if ($employee->department === 'Sales') {
    $emails[] = $employee->email;
    }
    }
    return $emails;
    }

    …map?

    View Slide

  50. function getSalesEmails($employees) {
    $emails = [];
    foreach ($employees as $employee) {
    if ($employee->department === 'Sales') {
    $emails[] = $employee->email;
    }
    }
    return $emails;
    }

    View Slide

  51. Thinking in
    STEPS

    View Slide

  52. function getSalesEmails($employees) {
    $emails = [];
    foreach ($employees as $employee) {
    if ($employee->department === 'Sales') {
    $emails[] = $employee->email;
    }
    }
    return $emails;
    }

    View Slide

  53. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = [];
    foreach ($employees as $employee) {
    if ($employee->department === 'Sales') {
    $emails[] = $employee->email;
    }
    }
    return $emails;
    }

    View Slide

  54. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = [];
    foreach ($employees as $employee) {
    if ($employee->department === 'Sales') {
    $emails[] = $employee->email;
    }
    }
    return $emails;
    }

    View Slide

  55. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = [];
    foreach ($employees as $employee) {
    if ($employee->department === 'Sales') {
    $emails[] = $employee->email;
    }
    }
    return $emails;
    }

    View Slide

  56. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = [];
    foreach ($salesEmployees as $employee) {
    if ($employee->department === 'Sales') {
    $emails[] = $employee->email;
    }
    }
    return $emails;
    }

    View Slide

  57. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = [];
    foreach ($salesEmployees as $employee) {
    if ($employee->department === 'Sales') {
    $emails[] = $employee->email;
    }
    }
    return $emails;
    }

    View Slide

  58. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = [];
    foreach ($salesEmployees as $employee) {
    $emails[] = $employee->email;
    }
    return $emails;
    }

    View Slide

  59. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = [];
    foreach ($salesEmployees as $employee) {
    $emails[] = $employee->email;
    }
    return $emails;
    }

    View Slide

  60. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = [];
    foreach ($salesEmployees as $employee) {
    $emails[] = $employee->email;
    }
    return $emails;
    }

    Map!

    View Slide

  61. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = [];
    foreach ($salesEmployees as $employee) {
    $emails[] = $employee->email;
    }
    return $emails;
    }

    View Slide

  62. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    return $emails;
    }

    View Slide

  63. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    return $emails;
    }

    View Slide

  64. Things I
    HATE

    View Slide

  65. #1. FOR LOOPS
    #2. CONDITIONALS
    #3. TEMP VARIABLES

    View Slide

  66. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    return $emails;
    }

    View Slide

  67. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    return $emails;
    }

    View Slide

  68. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    return $emails;
    }

    View Slide

  69. Inline
    TEMP

    View Slide

  70. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    return $emails;
    }

    View Slide

  71. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    return array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    }

    View Slide

  72. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    return array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    }

    View Slide

  73. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    return array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    }

    View Slide

  74. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    return array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    }

    View Slide

  75. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    return array_map(function ($employee) {
    return $employee->email;
    }, array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    }));
    }

    View Slide

  76. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    return array_map(function ($employee) {
    return $employee->email;
    }, array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    }));
    }

    View Slide

  77. function getSalesEmails($employees) {
    return array_map(function ($employee) {
    return $employee->email;
    }, array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    }));
    }

    View Slide

  78. The Problem With
    PRIMITIVES

    View Slide

  79. # => hello_there_laracon
    function toCamelCase($snakeString) {
    return lcfirst(
    str_replace(' ', '',
    ucwords(str_replace('_', ' ', $snakeString))
    )
    );
    }
    # => helloThereLaracon

    View Slide

  80. # => hello_there_laracon
    function toCamelCase($snakeString) {
    return $snakeString->replace('_', ' ')
    ->ucwords()
    ->replace(' ', '')
    ->lcfirst();
    }
    # => helloThereLaracon

    View Slide

  81. Introducing
    COLLECTIONS

    View Slide

  82. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    return $emails;
    }

    View Slide

  83. function getSalesEmails($employees) {
    $salesEmployees = array_filter($employees, function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    return $emails;
    }

    View Slide

  84. function getSalesEmails($employees) {
    $salesEmployees = $employees->filter(function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    return $emails;
    }

    View Slide

  85. function getSalesEmails($employees) {
    $salesEmployees = $employees->filter(function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = array_map(function ($employee) {
    return $employee->email;
    }, $salesEmployees);
    return $emails;
    }

    View Slide

  86. function getSalesEmails($employees) {
    $salesEmployees = $employees->filter(function ($employee) {
    return $employee->department == 'Sales';
    });
    $emails = $salesEmployees->map(function ($employee) {
    return $employee->email;
    });
    return $emails;
    }

    View Slide

  87. function getSalesEmails($employees) {
    return $employees->filter(function ($employee) {
    return $employee->department == 'Sales';
    })->map(function ($employee) {
    return $employee->email;
    });
    }

    View Slide

  88. Let’s write
    SOME CODE!

    View Slide

  89. http://www.fullstackradio.com
    FULL
    RADIO
    STACK

    View Slide

  90. @adamwathan
    adamwathan.me

    View Slide