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 full-size 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 full-size slide

  3. Higher Order
    FUNCTIONS

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  7. Afford more powerful
    ABSTRACTIONS

    View full-size slide

  8. For example…

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  42. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    …map?

    View full-size slide

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

    View full-size slide

  50. Thinking in
    STEPS

    View full-size slide

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

    View full-size slide

  52. 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 full-size 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 full-size 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 full-size slide

  55. 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 full-size 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 full-size slide

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

    View full-size 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 full-size 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;
    }

    Map!

    View full-size 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;
    }

    View full-size slide

  61. 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 full-size 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 full-size slide

  63. Things I
    HATE

    View full-size slide

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

    View full-size slide

  65. 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 full-size 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 full-size 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 full-size 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 full-size slide

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

    View full-size slide

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

    View full-size 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 full-size 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 full-size slide

  73. 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 full-size slide

  74. 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 full-size slide

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

    View full-size slide

  76. The Problem With
    PRIMITIVES

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  79. Introducing
    COLLECTIONS

    View full-size slide

  80. 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 full-size slide

  81. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  86. Let’s write
    SOME CODE!

    View full-size slide

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

    View full-size slide

  88. @adamwathan
    adamwathan.me

    View full-size slide