SlideShare a Scribd company logo
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

More Related Content

What's hot (20)

PDF
PHP Unit 4 arrays
Kumar
 
PDF
PHP 7 – What changed internally? (PHP Barcelona 2015)
Nikita Popov
 
PDF
Advanced Php - Macq Electronique 2010
Michelangelo van Dam
 
PPTX
Arrays in PHP
davidahaskins
 
PPTX
Bioinformatica p6-bioperl
Prof. Wim Van Criekinge
 
PPTX
Bioinformatics p5-bioperl v2013-wim_vancriekinge
Prof. Wim Van Criekinge
 
PDF
PHP data structures (and the impact of php 7 on them), phpDay Verona 2015, Italy
Patrick Allaert
 
PPTX
PHP Functions & Arrays
Henry Osborne
 
PDF
Database API, your new friend
kikoalonsob
 
PDF
DBIx::Class introduction - 2010
leo lapworth
 
PDF
What I learned from Seven Languages in Seven Weeks (IPRUG)
Kerry Buckley
 
PPT
Groovy unleashed
Isuru Samaraweera
 
PPTX
Chap 3php array part1
monikadeshmane
 
PDF
Perl object ?
ℕicolas ℝ.
 
PDF
Introductionto fp with groovy
Isuru Samaraweera
 
PDF
4.1 PHP Arrays
Jalpesh Vasa
 
PDF
Your code sucks, let's fix it
Rafael Dohms
 
PPTX
Chap 3php array part 2
monikadeshmane
 
PDF
Marc’s (bio)perl course
Marc Logghe
 
KEY
Potential Friend Finder
Richard Schneeman
 
PHP Unit 4 arrays
Kumar
 
PHP 7 – What changed internally? (PHP Barcelona 2015)
Nikita Popov
 
Advanced Php - Macq Electronique 2010
Michelangelo van Dam
 
Arrays in PHP
davidahaskins
 
Bioinformatica p6-bioperl
Prof. Wim Van Criekinge
 
Bioinformatics p5-bioperl v2013-wim_vancriekinge
Prof. Wim Van Criekinge
 
PHP data structures (and the impact of php 7 on them), phpDay Verona 2015, Italy
Patrick Allaert
 
PHP Functions & Arrays
Henry Osborne
 
Database API, your new friend
kikoalonsob
 
DBIx::Class introduction - 2010
leo lapworth
 
What I learned from Seven Languages in Seven Weeks (IPRUG)
Kerry Buckley
 
Groovy unleashed
Isuru Samaraweera
 
Chap 3php array part1
monikadeshmane
 
Perl object ?
ℕicolas ℝ.
 
Introductionto fp with groovy
Isuru Samaraweera
 
4.1 PHP Arrays
Jalpesh Vasa
 
Your code sucks, let's fix it
Rafael Dohms
 
Chap 3php array part 2
monikadeshmane
 
Marc’s (bio)perl course
Marc Logghe
 
Potential Friend Finder
Richard Schneeman
 

Similar to Banishing Loops with Functional Programming in PHP (20)

ODP
HTML Templates Using Clear Silver
PaulWay
 
PDF
TDD with PhpSpec
CiaranMcNulty
 
PDF
Rich Internet Applications con JavaFX e NetBeans
Fabrizio Giudici
 
PDF
Solr's Search Relevancy (Understand Solr's query debug)
Wongnai
 
KEY
Scaling php applications with redis
jimbojsb
 
PDF
Drupal 8: A story of growing up and getting off the island
Angela Byron
 
ODP
Perl Teach-In (part 2)
Dave Cross
 
PPTX
Quality code by design
WP Developers Club
 
PDF
WordPress London 16 May 2012 - You don’t know query
l3rady
 
PPTX
Building a horizontally scalable API in php
Wade Womersley
 
ODP
Php 102: Out with the Bad, In with the Good
Jeremy Kendall
 
PDF
[WLDN] Supercharging word press development in 2018
Adam Tomat
 
PPT
Intro to php
Sp Singh
 
PDF
RubyEnRails2007 - Dr Nic Williams - DIY Syntax
Dr Nic Williams
 
KEY
Intro to Ruby - Twin Cities Code Camp 7
Brian Hogan
 
PDF
Becoming a better WordPress Developer
Joey Kudish
 
PDF
Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...
ICS User Group
 
PDF
Modern php
Charles Anderson
 
PPT
MIND sweeping introduction to PHP
BUDNET
 
PDF
Caldera Learn - LoopConf WP API + Angular FTW Workshop
CalderaLearn
 
HTML Templates Using Clear Silver
PaulWay
 
TDD with PhpSpec
CiaranMcNulty
 
Rich Internet Applications con JavaFX e NetBeans
Fabrizio Giudici
 
Solr's Search Relevancy (Understand Solr's query debug)
Wongnai
 
Scaling php applications with redis
jimbojsb
 
Drupal 8: A story of growing up and getting off the island
Angela Byron
 
Perl Teach-In (part 2)
Dave Cross
 
Quality code by design
WP Developers Club
 
WordPress London 16 May 2012 - You don’t know query
l3rady
 
Building a horizontally scalable API in php
Wade Womersley
 
Php 102: Out with the Bad, In with the Good
Jeremy Kendall
 
[WLDN] Supercharging word press development in 2018
Adam Tomat
 
Intro to php
Sp Singh
 
RubyEnRails2007 - Dr Nic Williams - DIY Syntax
Dr Nic Williams
 
Intro to Ruby - Twin Cities Code Camp 7
Brian Hogan
 
Becoming a better WordPress Developer
Joey Kudish
 
Icsug dev day2014_road to damascus - conversion experience-lotusscript and @f...
ICS User Group
 
Modern php
Charles Anderson
 
MIND sweeping introduction to PHP
BUDNET
 
Caldera Learn - LoopConf WP API + Angular FTW Workshop
CalderaLearn
 
Ad

Recently uploaded (20)

PDF
Top 10 Testing Procedures to Ensure Your Magento to Shopify Migration Success...
CartCoders
 
PDF
Cleaning up your RPKI invalids, presented at PacNOG 35
APNIC
 
PPTX
Orchestrating things in Angular application
Peter Abraham
 
PPTX
PHIPA-Compliant Web Hosting in Toronto: What Healthcare Providers Must Know
steve198109
 
PDF
BRKACI-1003 ACI Brownfield Migration - Real World Experiences and Best Practi...
fcesargonca
 
PDF
BRKAPP-1102 - Proactive Network and Application Monitoring.pdf
fcesargonca
 
PDF
Enhancing Parental Roles in Protecting Children from Online Sexual Exploitati...
ICT Frame Magazine Pvt. Ltd.
 
DOCX
Custom vs. Off-the-Shelf Banking Software
KristenCarter35
 
PPTX
L1A Season 1 ENGLISH made by A hegy fixed
toszolder91
 
PDF
FutureCon Seattle 2025 Presentation Slides - You Had One Job
Suzanne Aldrich
 
PPTX
Metaphysics_Presentation_With_Visuals.pptx
erikjohnsales1
 
PPTX
法国巴黎第二大学本科毕业证{Paris 2学费发票Paris 2成绩单}办理方法
Taqyea
 
PPTX
Networking_Essentials_version_3.0_-_Module_3.pptx
ryan622010
 
PPTX
西班牙巴利阿里群岛大学电子版毕业证{UIBLetterUIB文凭证书}文凭复刻
Taqyea
 
PPTX
Softuni - Psychology of entrepreneurship
Kalin Karakehayov
 
PPTX
Lec15_Mutability Immutability-converted.pptx
khanjahanzaib1
 
PDF
BRKSP-2551 - Introduction to Segment Routing.pdf
fcesargonca
 
PPTX
Presentation3gsgsgsgsdfgadgsfgfgsfgagsfgsfgzfdgsdgs.pptx
SUB03
 
PDF
Boardroom AI: The Next 10 Moves | Cerebraix Talent Tech
ssuser73bdb11
 
PPTX
Networking_Essentials_version_3.0_-_Module_5.pptx
ryan622010
 
Top 10 Testing Procedures to Ensure Your Magento to Shopify Migration Success...
CartCoders
 
Cleaning up your RPKI invalids, presented at PacNOG 35
APNIC
 
Orchestrating things in Angular application
Peter Abraham
 
PHIPA-Compliant Web Hosting in Toronto: What Healthcare Providers Must Know
steve198109
 
BRKACI-1003 ACI Brownfield Migration - Real World Experiences and Best Practi...
fcesargonca
 
BRKAPP-1102 - Proactive Network and Application Monitoring.pdf
fcesargonca
 
Enhancing Parental Roles in Protecting Children from Online Sexual Exploitati...
ICT Frame Magazine Pvt. Ltd.
 
Custom vs. Off-the-Shelf Banking Software
KristenCarter35
 
L1A Season 1 ENGLISH made by A hegy fixed
toszolder91
 
FutureCon Seattle 2025 Presentation Slides - You Had One Job
Suzanne Aldrich
 
Metaphysics_Presentation_With_Visuals.pptx
erikjohnsales1
 
法国巴黎第二大学本科毕业证{Paris 2学费发票Paris 2成绩单}办理方法
Taqyea
 
Networking_Essentials_version_3.0_-_Module_3.pptx
ryan622010
 
西班牙巴利阿里群岛大学电子版毕业证{UIBLetterUIB文凭证书}文凭复刻
Taqyea
 
Softuni - Psychology of entrepreneurship
Kalin Karakehayov
 
Lec15_Mutability Immutability-converted.pptx
khanjahanzaib1
 
BRKSP-2551 - Introduction to Segment Routing.pdf
fcesargonca
 
Presentation3gsgsgsgsdfgadgsfgfgsfgagsfgsfgzfdgsdgs.pptx
SUB03
 
Boardroom AI: The Next 10 Moves | Cerebraix Talent Tech
ssuser73bdb11
 
Networking_Essentials_version_3.0_-_Module_5.pptx
ryan622010
 
Ad

Banishing Loops with Functional Programming in PHP

  • 2. 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
  • 3. In the beginning, there was code. And that was all... @davidbhayes from ThoughtfulCode.com
  • 4. As time goes on, we learn that maintaining code is hard @davidbhayes from ThoughtfulCode.com
  • 5. To ponder... Why is maintaining code hard? @davidbhayes from ThoughtfulCode.com
  • 6. Today, there are basically Three Paradigms @davidbhayes from ThoughtfulCode.com
  • 7. Procedural Code • You tell the computer exactly what to do • You might have "functions" which contain your common procedures @davidbhayes from ThoughtfulCode.com
  • 8. 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
  • 9. 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
  • 10. Functional Programming in a bit more depth @davidbhayes from ThoughtfulCode.com
  • 11. Pure functions... Read only their inputs @davidbhayes from ThoughtfulCode.com
  • 12. Pure functions... Affect only their outputs @davidbhayes from ThoughtfulCode.com
  • 13. Only touching inputs and 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 Talk About PHP @davidbhayes from ThoughtfulCode.com
  • 17. Most "classic" or "legacy" PHP is procedural @davidbhayes from ThoughtfulCode.com
  • 18. Most modern PHP code is object-oriented with procedural processes inside @davidbhayes from ThoughtfulCode.com
  • 19. I love foreach @davidbhayes from ThoughtfulCode.com
  • 20. foreach is fundamentally procedural (yes, even inside an object method) @davidbhayes from ThoughtfulCode.com
  • 21. 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
  • 22. How Does Functional Help? @davidbhayes from ThoughtfulCode.com
  • 23. What's map? Transform each entity in a list using a given function @davidbhayes from ThoughtfulCode.com
  • 24. What's filter? Keep items in a list if a function return true when run on an item @davidbhayes from ThoughtfulCode.com
  • 25. Getting Practical FP in PHP @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 this code... $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 this code... $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. Let's Talk about Callables @davidbhayes from ThoughtfulCode.com
  • 33. Simple function declarations are global function hello($name) { return 'Hello, '.$name; } Called like... echo hello('David'); @davidbhayes from ThoughtfulCode.com
  • 34. Anonymous functions can be saved to variables $hello = function($name) { return 'Hello, '.$name; } Called like... echo $hello('David'); @davidbhayes from ThoughtfulCode.com
  • 35. 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
  • 36. 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
  • 37. 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
  • 38. Let's Talk about use @davidbhayes from ThoughtfulCode.com
  • 40. This does not work! $nonlocal = 7; $greaterThan = function($number) { return $number > $nonlocal; } $greaterThan(1); @davidbhayes from ThoughtfulCode.com
  • 41. This also does not 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 is better! $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 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
  • 48. 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
  • 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 we can solve that... @davidbhayes from ThoughtfulCode.com
  • 52. Collections allow us to streamline filter, map, etc @davidbhayes from ThoughtfulCode.com
  • 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 order issues Which one's the array? @davidbhayes from ThoughtfulCode.com
  • 56. No need for temps with fluent interface ->chaining()->methods()->is()->cool(); @davidbhayes from ThoughtfulCode.com
  • 57. Your ORM may already have one... @davidbhayes from ThoughtfulCode.com
  • 58. 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
  • 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 • 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
  • 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 • flatMap is 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 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
  • 67. 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
  • 68. What makes code hard to maintain? • Short-lived variables are clutter • Disguised intent via foreach • Deeply nested conditionals @davidbhayes from ThoughtfulCode.com
  • 69. Does FP help? Local and temporary $variables are reduced, especially with pipelines @davidbhayes from ThoughtfulCode.com
  • 70. Does FP help? Replacing foreaches with map and filter makes it clearer what each does @davidbhayes from ThoughtfulCode.com
  • 71. Does FP help? Nesting is minimized by small, single purpose functions @davidbhayes from ThoughtfulCode.com
  • 72. "A program cannot change until it is alive in a programmer's head." —Jessica Kerr on Ruby Rogues (via @johnkary) @davidbhayes from ThoughtfulCode.com
  • 73. Localizing control and complexity makes it easier for you to jump in between meetings, children, life, etc @davidbhayes from ThoughtfulCode.com
  • 74. 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
  • 75. 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
  • 76. Thank You!I've been @davidbhayes I run a site called WPShout and another called Thoughtful Code @davidbhayes from ThoughtfulCode.com