Banishing Loops with Functional PHP @davidbhayes @davidbhayes from ThoughtfulCode.com
Goals To understand... 1. what functional programming is 2. practical substitutions we can make in our code using it 3. why it can be beneficial to make these changes @davidbhayes from ThoughtfulCode.com
In the beginning, there was code. And that was all... @davidbhayes from ThoughtfulCode.com
As time goes on, we learn that maintaining code is hard @davidbhayes from ThoughtfulCode.com
To ponder... Why is maintaining code hard? @davidbhayes from ThoughtfulCode.com
Today, there are basically Three Paradigms @davidbhayes from ThoughtfulCode.com
Procedural Code • You tell the computer exactly what to do • You might have "functions" which contain your common procedures @davidbhayes from ThoughtfulCode.com
OO Code • You use the inherent human understanding of objects to make code more comprehensible • In PHP, you use classes to define blueprint for instances of objects @davidbhayes from ThoughtfulCode.com
Functional Code • Mathematical-functions have magic powers, pure functions are more unitary than procedures • All pure functions are stateless and create only their own output @davidbhayes from ThoughtfulCode.com
Functional Programming in a bit more depth @davidbhayes from ThoughtfulCode.com
Pure functions... Read only their inputs @davidbhayes from ThoughtfulCode.com
Pure functions... Affect only their outputs @davidbhayes from ThoughtfulCode.com
Only touching inputs and outputs means • No DB changes • No files created • No reading of global state (like time) @davidbhayes from ThoughtfulCode.com
It also means... • Functions are completely idempotent • Functions can be composed • Rerunning a function for debugging requires only knowing its name and parameters @davidbhayes from ThoughtfulCode.com
Functional programming languages • Haskell • Elm • Clojure • Scala (sort of) • JavaScript (sort of) @davidbhayes from ThoughtfulCode.com
Now, Lets Talk About PHP @davidbhayes from ThoughtfulCode.com
Most "classic" or "legacy" PHP is procedural @davidbhayes from ThoughtfulCode.com
Most modern PHP code is object-oriented with procedural processes inside @davidbhayes from ThoughtfulCode.com
I love foreach @davidbhayes from ThoughtfulCode.com
foreach is fundamentally procedural (yes, even inside an object method) @davidbhayes from ThoughtfulCode.com
A classic PHP pattern: $saved = []; foreach($items as $item) { if ($item->size > 5) { $saved[] = $item; } } $display_titles = []; foreach($saved as $item) { $display_titles[] = ucfirst($item->title); } foreach($display_titles as $title) { echo '<h1>'.$title.'</h1>'; } @davidbhayes from ThoughtfulCode.com
How Does Functional Help? @davidbhayes from ThoughtfulCode.com
What's map? Transform each entity in a list using a given function @davidbhayes from ThoughtfulCode.com
What's filter? Keep items in a list if a function return true when run on an item @davidbhayes from ThoughtfulCode.com
Getting Practical FP in PHP @davidbhayes from ThoughtfulCode.com
array_map // array_map ( callable $callback , array $array1 [, array $... ] ) $start = [1, 2, 3, 4, 5]; $end = array_map(function ($i) { return $i * 2; }, $start); // [2, 4, 6, 8, 10] @davidbhayes from ThoughtfulCode.com
array_map makes this code... $start = [1, 2, 3, 4, 5]; $end = []; foreach($start as $i) { $end[] = $i * 2; } @davidbhayes from ThoughtfulCode.com
Into this $start = [1, 2, 3, 4, 5]; $end = array_map(function ($i) { return $i * 2; }, $start); @davidbhayes from ThoughtfulCode.com
array_filter // array_filter ( array $array [, callable $callback [, int $flag = 0 ]] ) $start = [1, 2, 3, 4, 5, 6]; $even = array_filter($start, function ($i) { return $i % 2 === 0; }); // [2, 4, 6] @davidbhayes from ThoughtfulCode.com
array_filter makes this code... $start = [1, 2, 3, 4, 5, 6]; $even = []; foreach($start as $i) { if ($i % 2 === 0) { $even[] = $i; } } @davidbhayes from ThoughtfulCode.com
Into this $start = [1, 2, 3, 4, 5, 6]; $even = array_filter($start, function ($i) { return $i % 2 === 0; }); @davidbhayes from ThoughtfulCode.com
Let's Talk about Callables @davidbhayes from ThoughtfulCode.com
Simple function declarations are global function hello($name) { return 'Hello, '.$name; } Called like... echo hello('David'); @davidbhayes from ThoughtfulCode.com
Anonymous functions can be saved to variables $hello = function($name) { return 'Hello, '.$name; } Called like... echo $hello('David'); @davidbhayes from ThoughtfulCode.com
Classes can have static methods class Namer { public static function hello($name) { return 'Hello, '.$name; } } Called like... echo Namer::hello('David'); @davidbhayes from ThoughtfulCode.com
Objects can have methods class Namer { public function hello($name) { return 'Hello, '.$name; } } Called like... $namer = new Namer; echo $namer->hello('David'); @davidbhayes from ThoughtfulCode.com
All forms can be used with array_map, etc $names = ['David', 'Megan', 'Sierra']; array_map('hello', $names); array_map($hello, $names); array_map('Namer::hello', $names); array_map(['Namer', 'hello'], $names); $namer = new Namer; array_map([$namer, 'hello'], $names); @davidbhayes from ThoughtfulCode.com
Let's Talk about use @davidbhayes from ThoughtfulCode.com
Variable Scoping @davidbhayes from ThoughtfulCode.com
This does not work! $nonlocal = 7; $greaterThan = function($number) { return $number > $nonlocal; } $greaterThan(1); @davidbhayes from ThoughtfulCode.com
This also does not work! global $nonlocal = 7; $greaterThan = function($number) { return $number > $nonlocal; } $greaterThan(1); @davidbhayes from ThoughtfulCode.com
This does work! global $nonlocal = 7; $greaterThan = function($number) { global $nonlocal; return $number > $nonlocal; } $greaterThan(1); @davidbhayes from ThoughtfulCode.com
But this is better! $nonlocal = 7; $greaterThan = function($number) use ($nonlocal) { return $number > $nonlocal; } $greaterThan(1); @davidbhayes from ThoughtfulCode.com
Why this matters $nonlocal = 7; $greaterThan = function($number) use ($nonlocal) { return $number > $nonlocal; } array_filter($array, $greaterThan); @davidbhayes from ThoughtfulCode.com
Or else $nonlocal = 7; array_filter($array, function($number) use ($nonlocal) { return $number > $nonlocal; }); @davidbhayes from ThoughtfulCode.com
Remember this? $saved = []; foreach($items as $item) { if ($item->size > 5) { $saved[] = $item; } } $display_titles = []; foreach($saved as $item) { $display_titles[] = ucfirst($item->title); } foreach($display_titles as $title) { echo '<h1>'.$title.'</h1>'; } @davidbhayes from ThoughtfulCode.com
Let's transform filter in for foreach $saved = array_filter($items, function($i) { return $item->size > 5; }); $display_titles = []; foreach($saved as $item) { $display_titles[] = ucfirst($item->title); } foreach($display_titles as $title) { echo '<h1>'.$title.'</h1>'; } @davidbhayes from ThoughtfulCode.com
Let's transform map for capitalizing $saved = array_filter($items, function($i) { return $i->size > 5; }); $display_titles = array_map(function($item) { return ucfirst($item->title); }, $saved); // somewhere else foreach($display_titles as $title) { echo '<h1>'.$title.'</h1>'; } @davidbhayes from ThoughtfulCode.com
Hassles with that • Temporary variables at every step • Argument order on array_filter and array_map is inconsistent @davidbhayes from ThoughtfulCode.com
But maybe we can solve that... @davidbhayes from ThoughtfulCode.com
Collection Libraries @davidbhayes from ThoughtfulCode.com
Collections allow us to streamline filter, map, etc @davidbhayes from ThoughtfulCode.com
Why collection pipelines? @davidbhayes from ThoughtfulCode.com
Easier to read -- we read left to right in English array_map(array_filter()) executes inside out @davidbhayes from ThoughtfulCode.com
Skip argument order issues Which one's the array? @davidbhayes from ThoughtfulCode.com
No need for temps with fluent interface ->chaining()->methods()->is()->cool(); @davidbhayes from ThoughtfulCode.com
Your ORM may already have one... @davidbhayes from ThoughtfulCode.com
Using a Laravel Collection We have a Reddit-like site: • Posts have scores • Comments have scores • We have a 'fluency scorer' • We have a hypothesis that good posts get higher scoring and more fluent comments @davidbhayes from ThoughtfulCode.com
Without FP $posts = Posts::all(); $goodPosts = []; foreach($posts as $post) { if ($post->score > 500) { $goodPosts[] = $post; } } $goodComments = []; foreach($goodPosts as $post) { $comments = $post->comments; foreach($comments as $c) { if ($c->score > 100) { $goodComments[] = $c; } } } $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
Refactor 1 - filter out posts $goodPosts = Posts::all() ->filter(function($post) { return $post->score > 500 }); $goodComments = []; foreach($goodPosts as $post) { $comments = $post->comments; foreach($comments as $c) { if ($c->score > 100) { $goodComments[] = $c; } } } $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
Quick Explanation • flatten will take nested arrays and de-nest them • PHP doesn't have an native array_flatten, but you can make one @davidbhayes from ThoughtfulCode.com
Refactor 2 - collect good comments on good posts $goodComments = Posts::all() ->filter(function($post) { return $post->score > 500 }) ->map(function($p) { return $post->comments; }) ->flatten() ->filter(function($c) { return $c->score > 100; }); $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
Quick Simplification • flatMap is a shortcut for map then flatten @davidbhayes from ThoughtfulCode.com
Refactor 2 - collect good comments on good posts (again) $goodComments = Posts::all() ->filter(function($post) { return $post->score > 500 }) ->flatMap(function($p) { return $post->comments; }) ->filter(function($c) { return $c->score > 100; }); $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
Refactor 3 - create new set with a map $scoredGoodCommments = Posts::all() ->filter(function($post) { return $post->score > 500 }) ->flatMap(function($p) { return $post->comments; }) ->filter(function($c) { return $c->score > 100; }) ->map(function($c) { return [ 'score' => $c->score, 'fluency' => FluentScorer::score($c->content), 'comment' => $c, 'post' => $c->post, ]; }); @davidbhayes from ThoughtfulCode.com
Did it get better? (original) $posts = Posts::all(); $goodPosts = []; foreach($posts as $post) { if ($post->score > 500) { $goodPosts[] = $post; } } $goodComments = []; foreach($goodPosts as $post) { $comments = $post->comments; foreach($comments as $c) { if ($c->score > 100) { $goodComments[] = $c; } } } $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
Did it get better? (final) $scoredGoodCommments = Posts::all() ->filter(function($post) { return $post->score > 500 }) ->flatMap(function($p) { return $post->comments; }) ->filter(function($c) { return $c->score > 100; }) ->map(function($c) { return [ 'score' => $c->score, 'fluency' => FluentScorer::score($c->content), 'comment' => $c, 'post' => $c->post, ]; }); @davidbhayes from ThoughtfulCode.com
What makes code hard to maintain? • Short-lived variables are clutter • Disguised intent via foreach • Deeply nested conditionals @davidbhayes from ThoughtfulCode.com
Does FP help? Local and temporary $variables are reduced, especially with pipelines @davidbhayes from ThoughtfulCode.com
Does FP help? Replacing foreaches with map and filter makes it clearer what each does @davidbhayes from ThoughtfulCode.com
Does FP help? Nesting is minimized by small, single purpose functions @davidbhayes from ThoughtfulCode.com
"A program cannot change until it is alive in a programmer's head." —Jessica Kerr on Ruby Rogues (via @johnkary) @davidbhayes from ThoughtfulCode.com
Localizing control and complexity makes it easier for you to jump in between meetings, children, life, etc @davidbhayes from ThoughtfulCode.com
Again Did it get better? (original) $posts = Posts::all(); $goodPosts = []; foreach($posts as $post) { if ($post->score > 500) { $goodPosts[] = $post; } } $goodComments = []; foreach($goodPosts as $post) { $comments = $post->comments; foreach($comments as $c) { if ($c->score > 100) { $goodComments[] = $c; } } } $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
Did it get better? (final) $scoredGoodCommments = Posts::all() ->filter(function($post) { return $post->score > 500 }) ->flatMap(function($p) { return $post->comments; }) ->filter(function($c) { return $c->score > 100; }) ->map(function($c) { return [ 'score' => $c->score, 'fluency' => FluentScorer::score($c->content), 'comment' => $c, 'post' => $c->post, ]; }); @davidbhayes from ThoughtfulCode.com
Thank You!I've been @davidbhayes I run a site called WPShout and another called Thoughtful Code @davidbhayes from ThoughtfulCode.com

Banishing Loops with Functional Programming in PHP

  • 1.
  • 2.
    Goals To understand... 1. whatfunctional programming is 2. practical substitutions we can make in our code using it 3. why it can be beneficial to make these changes @davidbhayes from ThoughtfulCode.com
  • 3.
    In the beginning,there was code. And that was all... @davidbhayes from ThoughtfulCode.com
  • 4.
    As time goeson, we learn that maintaining code is hard @davidbhayes from ThoughtfulCode.com
  • 5.
    To ponder... Why is maintaining codehard? @davidbhayes from ThoughtfulCode.com
  • 6.
    Today, there arebasically Three Paradigms @davidbhayes from ThoughtfulCode.com
  • 7.
    Procedural Code • Youtell the computer exactly what to do • You might have "functions" which contain your common procedures @davidbhayes from ThoughtfulCode.com
  • 8.
    OO Code • Youuse the inherent human understanding of objects to make code more comprehensible • In PHP, you use classes to define blueprint for instances of objects @davidbhayes from ThoughtfulCode.com
  • 9.
    Functional Code • Mathematical-functionshave magic powers, pure functions are more unitary than procedures • All pure functions are stateless and create only their own output @davidbhayes from ThoughtfulCode.com
  • 10.
    Functional Programming in abit more depth @davidbhayes from ThoughtfulCode.com
  • 11.
    Pure functions... Read onlytheir inputs @davidbhayes from ThoughtfulCode.com
  • 12.
    Pure functions... Affect onlytheir outputs @davidbhayes from ThoughtfulCode.com
  • 13.
    Only touching inputsand outputs means • No DB changes • No files created • No reading of global state (like time) @davidbhayes from ThoughtfulCode.com
  • 14.
    It also means... •Functions are completely idempotent • Functions can be composed • Rerunning a function for debugging requires only knowing its name and parameters @davidbhayes from ThoughtfulCode.com
  • 15.
    Functional programming languages •Haskell • Elm • Clojure • Scala (sort of) • JavaScript (sort of) @davidbhayes from ThoughtfulCode.com
  • 16.
    Now, Lets TalkAbout PHP @davidbhayes from ThoughtfulCode.com
  • 17.
    Most "classic" or"legacy" PHP is procedural @davidbhayes from ThoughtfulCode.com
  • 18.
    Most modern PHPcode is object-oriented with procedural processes inside @davidbhayes from ThoughtfulCode.com
  • 19.
    I love foreach @davidbhayesfrom ThoughtfulCode.com
  • 20.
    foreach is fundamentally procedural (yes,even inside an object method) @davidbhayes from ThoughtfulCode.com
  • 21.
    A classic PHPpattern: $saved = []; foreach($items as $item) { if ($item->size > 5) { $saved[] = $item; } } $display_titles = []; foreach($saved as $item) { $display_titles[] = ucfirst($item->title); } foreach($display_titles as $title) { echo '<h1>'.$title.'</h1>'; } @davidbhayes from ThoughtfulCode.com
  • 22.
  • 23.
    What's map? Transform eachentity in a list using a given function @davidbhayes from ThoughtfulCode.com
  • 24.
    What's filter? Keep itemsin a list if a function return true when run on an item @davidbhayes from ThoughtfulCode.com
  • 25.
    Getting Practical FP inPHP @davidbhayes from ThoughtfulCode.com
  • 26.
    array_map // array_map (callable $callback , array $array1 [, array $... ] ) $start = [1, 2, 3, 4, 5]; $end = array_map(function ($i) { return $i * 2; }, $start); // [2, 4, 6, 8, 10] @davidbhayes from ThoughtfulCode.com
  • 27.
    array_map makes thiscode... $start = [1, 2, 3, 4, 5]; $end = []; foreach($start as $i) { $end[] = $i * 2; } @davidbhayes from ThoughtfulCode.com
  • 28.
    Into this $start =[1, 2, 3, 4, 5]; $end = array_map(function ($i) { return $i * 2; }, $start); @davidbhayes from ThoughtfulCode.com
  • 29.
    array_filter // array_filter (array $array [, callable $callback [, int $flag = 0 ]] ) $start = [1, 2, 3, 4, 5, 6]; $even = array_filter($start, function ($i) { return $i % 2 === 0; }); // [2, 4, 6] @davidbhayes from ThoughtfulCode.com
  • 30.
    array_filter makes thiscode... $start = [1, 2, 3, 4, 5, 6]; $even = []; foreach($start as $i) { if ($i % 2 === 0) { $even[] = $i; } } @davidbhayes from ThoughtfulCode.com
  • 31.
    Into this $start =[1, 2, 3, 4, 5, 6]; $even = array_filter($start, function ($i) { return $i % 2 === 0; }); @davidbhayes from ThoughtfulCode.com
  • 32.
  • 33.
    Simple function declarationsare global function hello($name) { return 'Hello, '.$name; } Called like... echo hello('David'); @davidbhayes from ThoughtfulCode.com
  • 34.
    Anonymous functions canbe saved to variables $hello = function($name) { return 'Hello, '.$name; } Called like... echo $hello('David'); @davidbhayes from ThoughtfulCode.com
  • 35.
    Classes can havestatic methods class Namer { public static function hello($name) { return 'Hello, '.$name; } } Called like... echo Namer::hello('David'); @davidbhayes from ThoughtfulCode.com
  • 36.
    Objects can havemethods class Namer { public function hello($name) { return 'Hello, '.$name; } } Called like... $namer = new Namer; echo $namer->hello('David'); @davidbhayes from ThoughtfulCode.com
  • 37.
    All forms canbe used with array_map, etc $names = ['David', 'Megan', 'Sierra']; array_map('hello', $names); array_map($hello, $names); array_map('Namer::hello', $names); array_map(['Namer', 'hello'], $names); $namer = new Namer; array_map([$namer, 'hello'], $names); @davidbhayes from ThoughtfulCode.com
  • 38.
    Let's Talk aboutuse @davidbhayes from ThoughtfulCode.com
  • 39.
  • 40.
    This does notwork! $nonlocal = 7; $greaterThan = function($number) { return $number > $nonlocal; } $greaterThan(1); @davidbhayes from ThoughtfulCode.com
  • 41.
    This also doesnot work! global $nonlocal = 7; $greaterThan = function($number) { return $number > $nonlocal; } $greaterThan(1); @davidbhayes from ThoughtfulCode.com
  • 42.
    This does work! global$nonlocal = 7; $greaterThan = function($number) { global $nonlocal; return $number > $nonlocal; } $greaterThan(1); @davidbhayes from ThoughtfulCode.com
  • 43.
    But this isbetter! $nonlocal = 7; $greaterThan = function($number) use ($nonlocal) { return $number > $nonlocal; } $greaterThan(1); @davidbhayes from ThoughtfulCode.com
  • 44.
    Why this matters $nonlocal= 7; $greaterThan = function($number) use ($nonlocal) { return $number > $nonlocal; } array_filter($array, $greaterThan); @davidbhayes from ThoughtfulCode.com
  • 45.
    Or else $nonlocal =7; array_filter($array, function($number) use ($nonlocal) { return $number > $nonlocal; }); @davidbhayes from ThoughtfulCode.com
  • 46.
    Remember this? $saved =[]; foreach($items as $item) { if ($item->size > 5) { $saved[] = $item; } } $display_titles = []; foreach($saved as $item) { $display_titles[] = ucfirst($item->title); } foreach($display_titles as $title) { echo '<h1>'.$title.'</h1>'; } @davidbhayes from ThoughtfulCode.com
  • 47.
    Let's transform filter infor foreach $saved = array_filter($items, function($i) { return $item->size > 5; }); $display_titles = []; foreach($saved as $item) { $display_titles[] = ucfirst($item->title); } foreach($display_titles as $title) { echo '<h1>'.$title.'</h1>'; } @davidbhayes from ThoughtfulCode.com
  • 48.
    Let's transform map forcapitalizing $saved = array_filter($items, function($i) { return $i->size > 5; }); $display_titles = array_map(function($item) { return ucfirst($item->title); }, $saved); // somewhere else foreach($display_titles as $title) { echo '<h1>'.$title.'</h1>'; } @davidbhayes from ThoughtfulCode.com
  • 49.
    Hassles with that •Temporary variables at every step • Argument order on array_filter and array_map is inconsistent @davidbhayes from ThoughtfulCode.com
  • 50.
    But maybe wecan solve that... @davidbhayes from ThoughtfulCode.com
  • 51.
  • 52.
    Collections allow usto streamline filter, map, etc @davidbhayes from ThoughtfulCode.com
  • 53.
  • 54.
    Easier to read-- we read left to right in English array_map(array_filter()) executes inside out @davidbhayes from ThoughtfulCode.com
  • 55.
    Skip argument orderissues Which one's the array? @davidbhayes from ThoughtfulCode.com
  • 56.
    No need fortemps with fluent interface ->chaining()->methods()->is()->cool(); @davidbhayes from ThoughtfulCode.com
  • 57.
    Your ORM may alreadyhave one... @davidbhayes from ThoughtfulCode.com
  • 58.
    Using a LaravelCollection We have a Reddit-like site: • Posts have scores • Comments have scores • We have a 'fluency scorer' • We have a hypothesis that good posts get higher scoring and more fluent comments @davidbhayes from ThoughtfulCode.com
  • 59.
    Without FP $posts =Posts::all(); $goodPosts = []; foreach($posts as $post) { if ($post->score > 500) { $goodPosts[] = $post; } } $goodComments = []; foreach($goodPosts as $post) { $comments = $post->comments; foreach($comments as $c) { if ($c->score > 100) { $goodComments[] = $c; } } } $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
  • 60.
    Refactor 1 -filter out posts $goodPosts = Posts::all() ->filter(function($post) { return $post->score > 500 }); $goodComments = []; foreach($goodPosts as $post) { $comments = $post->comments; foreach($comments as $c) { if ($c->score > 100) { $goodComments[] = $c; } } } $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
  • 61.
    Quick Explanation • flattenwill take nested arrays and de-nest them • PHP doesn't have an native array_flatten, but you can make one @davidbhayes from ThoughtfulCode.com
  • 62.
    Refactor 2 -collect good comments on good posts $goodComments = Posts::all() ->filter(function($post) { return $post->score > 500 }) ->map(function($p) { return $post->comments; }) ->flatten() ->filter(function($c) { return $c->score > 100; }); $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
  • 63.
    Quick Simplification • flatMapis a shortcut for map then flatten @davidbhayes from ThoughtfulCode.com
  • 64.
    Refactor 2 -collect good comments on good posts (again) $goodComments = Posts::all() ->filter(function($post) { return $post->score > 500 }) ->flatMap(function($p) { return $post->comments; }) ->filter(function($c) { return $c->score > 100; }); $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
  • 65.
    Refactor 3 -create new set with a map $scoredGoodCommments = Posts::all() ->filter(function($post) { return $post->score > 500 }) ->flatMap(function($p) { return $post->comments; }) ->filter(function($c) { return $c->score > 100; }) ->map(function($c) { return [ 'score' => $c->score, 'fluency' => FluentScorer::score($c->content), 'comment' => $c, 'post' => $c->post, ]; }); @davidbhayes from ThoughtfulCode.com
  • 66.
    Did it getbetter? (original) $posts = Posts::all(); $goodPosts = []; foreach($posts as $post) { if ($post->score > 500) { $goodPosts[] = $post; } } $goodComments = []; foreach($goodPosts as $post) { $comments = $post->comments; foreach($comments as $c) { if ($c->score > 100) { $goodComments[] = $c; } } } $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
  • 67.
    Did it getbetter? (final) $scoredGoodCommments = Posts::all() ->filter(function($post) { return $post->score > 500 }) ->flatMap(function($p) { return $post->comments; }) ->filter(function($c) { return $c->score > 100; }) ->map(function($c) { return [ 'score' => $c->score, 'fluency' => FluentScorer::score($c->content), 'comment' => $c, 'post' => $c->post, ]; }); @davidbhayes from ThoughtfulCode.com
  • 68.
    What makes codehard to maintain? • Short-lived variables are clutter • Disguised intent via foreach • Deeply nested conditionals @davidbhayes from ThoughtfulCode.com
  • 69.
    Does FP help? Localand temporary $variables are reduced, especially with pipelines @davidbhayes from ThoughtfulCode.com
  • 70.
    Does FP help? Replacingforeaches with map and filter makes it clearer what each does @davidbhayes from ThoughtfulCode.com
  • 71.
    Does FP help? Nestingis minimized by small, single purpose functions @davidbhayes from ThoughtfulCode.com
  • 72.
    "A program cannot changeuntil it is alive in a programmer's head." —Jessica Kerr on Ruby Rogues (via @johnkary) @davidbhayes from ThoughtfulCode.com
  • 73.
    Localizing control andcomplexity makes it easier for you to jump in between meetings, children, life, etc @davidbhayes from ThoughtfulCode.com
  • 74.
    Again Did it getbetter? (original) $posts = Posts::all(); $goodPosts = []; foreach($posts as $post) { if ($post->score > 500) { $goodPosts[] = $post; } } $goodComments = []; foreach($goodPosts as $post) { $comments = $post->comments; foreach($comments as $c) { if ($c->score > 100) { $goodComments[] = $c; } } } $scoredGoodComments = []; foreach($goodComments as $c) { $local['score'] = $c->score; $local['fluency'] = FluentScorer::score($c->content); $local['comment'] = $c; $local['post'] = $c->post; $scoredGoodComments[] = $local; } @davidbhayes from ThoughtfulCode.com
  • 75.
    Did it getbetter? (final) $scoredGoodCommments = Posts::all() ->filter(function($post) { return $post->score > 500 }) ->flatMap(function($p) { return $post->comments; }) ->filter(function($c) { return $c->score > 100; }) ->map(function($c) { return [ 'score' => $c->score, 'fluency' => FluentScorer::score($c->content), 'comment' => $c, 'post' => $c->post, ]; }); @davidbhayes from ThoughtfulCode.com
  • 76.
    Thank You!I've been@davidbhayes I run a site called WPShout and another called Thoughtful Code @davidbhayes from ThoughtfulCode.com