Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Being functional in PHP
David de Boer
functional
functions
y = f(x)
f: X → Y
functional programming
functional thinking
functional communication
not languages
Why should you care?
f(x)
Others
PHP
C++
C
Java
– Robert C. Martin
“There is a freight train barreling
down the tracks towards us, with
multi-core emblazoned on it; and
y...
λ
closures
__invoke
2001 2009 2011 2012
array_map
array_filter
callable
Slim
Silex
middlewaresSymfony2
2013 2014 2015 2016
PSR-7
anonymous
classes
foreach
promises
futuresStackPHP
HTTP
middleware

proposal
“The limits of my language
mean the limits of my world.”
– Ludwig Wittgenstein
I’m David
/ddeboer
@ddeboer_nl
Erlang: The Movie
Erlang
Concurrent
Passing messages
Fault-tolerant
Let’s begin$ brew install erlang
$ erl
Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:4:4] [async-threads:
10] [hipe] [...
➊
<?php
$sum = 0;
for ($i = 1; $i <= 5; $i++) {
$sum = $sum + $i;
}
echo $sum;
// 15
-module(math).
-export([sum/1]).
sum(Number) ->
Sum = 0,
Numbers = lists:seq(1, Number),
lists:foreach(
fun(N) ->
Sum = Su...
1> X = 5.
5
2> X.
5
3> X = X * 2.
** exception error: no match of right hand side value 10
4> 5 = 10.
** exception error: ...
1> lists:sum(lists:seq(1, 5)).
15
➋
Imperative<?php
$sum = 0;
for ($i = 1; $i <= 5; $i++) {
$sum = $sum + $i;
}
iteration
keeps
changing
-module(math2).
-export([sum2/1]).
sum2(Number) ->
List = lists:seq(1, Number),
sum2(List, 0).
sum2([], Sum) -> Sum;
sum2(...
Declarative 1<?php
// Declare a function!
function sum($x)
{
if ($x == 0) {
return $x;
}
return $x + sum($x - 1);
}
sum(5)...
Declarative 2<?php
function sum2($number)
{
array_sum(range(1, $number));
}
echo sum2(5);
// yuuuup, 15 again
functionfunc...
Some history
Church Von Neumann
declarative imperative
A lot of our code is about the
hardware it runs on
But programmers should worry

about the conceptual problem domain
Recursion-module(math).
-export([fac/1]).
fac(0) -> 1;
fac(N) -> N * fac(N - 1).
1> math:fac(9).
362880
function
calls its...
Recursion fail-module(math).
-export([fac/1]).
fac(0) -> 1;
fac(N) -> N * fac(N - 1).
%%fac(9) = 9 * fac(9 - 1)
%% = 9 * 8...
Tail recursion-module(math).
-export([tail_fac/1]).
tail_fac(N) -> tail_fac(N, 1).
tail_fac(0, Acc) -> Acc;
tail_fac(N, Ac...
➌
$object->setFoo(5);
$value = $object->getFoo();
no
return
value
no input
value
$object->setFoo(5);
$value = $object->getFoo();
$object->setFoo('Change of state!');
$value = $object->getFoo();
no
return...
No side-effects
A pure function
does not rely on data outside itself
does not change data outside itself
Object orientation
Solve problems with objects
Objects have internal state
Modify state through methods
With side-effectsclass Todo
{
public $status = 'todo';
}
function finish(Todo $task)
{
$task->status = 'done';
}
$uhoh = ne...
No side-effectsclass Todo
{
public $status = 'todo';
}
function finish(Todo $task)
{
$copy = clone $task;
$copy->status = '...
Some state 

must change
Read/write database
Get user input
Current date and time
Random values (security)
Immutable objects
PSR-7 HTTP messages
Domain value objects
DateTimeImmutable
Service objects
Immutable infrastructure
declarative configuration (Puppet, Chef)
containers (Docker)
➍
Higher-order functions
Functions are values
so they can be arguments
and return values
$names = ['Billy', 'Bob', 'Thornton'];
$anonymise = anonymise('sha256');
var_dump(array_map($anonymise, $names));
// array...
$names = ['Billy', 'Bob', 'Thornton'];
$anonymise = anonymise('sha256');
var_dump(array_map($anonymise, $names));
// array...
Middleware<?php
use PsrHttpMessageRequestInterface;
function add_header($header, $value)
{
return function (callable $hand...
Middleware<?php
use PsrHttpMessageServerRequestInterface as Request;
use PsrHttpMessageResponseInterface as Response;
$app...
Typehints?
Middleware<?php
use PsrHttpMessageRequestInterface as Request;
use PsrHttpMessageResponseInterface as Response;
$middlewar...
Middleware<?php
use PsrHttpMessageRequestInterface as Request;
use PsrHttpMessageResponseInterface as Response;
$middlewar...
kapolos/pramda
Middleware<?php
$planets = [
[
"name" => "Earth",
"order" => 3,
"has" => ["moon", "oreos"],
"contact" => [
"name" => "Bob ...
➎
Composition over
inheritance
Single responsibility
and
interface segregation
Service objects<?php
namespace SymfonyComponentSecurityCore;
interface SecurityContextInterface
{
public function getToken...
Service objects<?php
namespace SymfonyComponentSecurityCoreAuthenticationTokenStorage;
interface TokenStorageInterface
{
p...
Single responsibility
taken to its
logical conclusion
You may not need
all those patterns
Wrap up
➊ No assignment
➋ Declarative
➌ Side-effects



➍ Higher-order functions



➎ Patterns
Take-aways
Reduce and isolate side-effects
Create immutable value objects
Be declarative
More?
https://joind.in/talk/8dfe1
@ddeboer_nl
Questions?
Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)
Upcoming SlideShare
Loading in …5
×

Being functional in PHP (DPC 2016)

365 views

Published on

Functional programming, though far from new, has gained much traction recently. Functional programming characteristics have started to appear in the PHP world, too. Microframeworks including Silex and Slim, middleware architectures (Stack) and even standards (PSR-7) rely on concepts such as lambdas, referential transparency and immutability, all of which come from functional programming.

I’ll give you a crash course in Erlang, a pragmatic functional language to make you feel familiar with the functional paradigm. By comparing code samples between Erlang and PHP, you’ll find out how and why you should employ functional programming in your PHP applications. You’ll see that functional programming is nothing to be scared of. On the contrary, understanding its concepts broadens your programming horizon and provides you with valuable solutions to your problems.

Published in: Software
  • Get Now to Read PDF eBook === http://zakuratest.com/Functional-PHP.html
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Being functional in PHP (DPC 2016)

  1. 1. Being functional in PHP David de Boer
  2. 2. functional
  3. 3. functions
  4. 4. y = f(x) f: X → Y
  5. 5. functional programming functional thinking functional communication not languages
  6. 6. Why should you care?
  7. 7. f(x) Others PHP C++ C Java
  8. 8. – Robert C. Martin “There is a freight train barreling down the tracks towards us, with multi-core emblazoned on it; and you’d better be ready by the time it gets here.”
  9. 9. λ closures __invoke 2001 2009 2011 2012 array_map array_filter callable Slim Silex middlewaresSymfony2
  10. 10. 2013 2014 2015 2016 PSR-7 anonymous classes foreach promises futuresStackPHP HTTP middleware
 proposal
  11. 11. “The limits of my language mean the limits of my world.” – Ludwig Wittgenstein
  12. 12. I’m David /ddeboer @ddeboer_nl
  13. 13. Erlang: The Movie
  14. 14. Erlang Concurrent Passing messages Fault-tolerant
  15. 15. Let’s begin$ brew install erlang $ erl Erlang/OTP 18 [erts-7.2.1] [source] [64-bit] [smp:4:4] [async-threads: 10] [hipe] [kernel-poll:false] [dtrace] Eshell V7.2.1 (abort with ^G) 1> on OS X REPL
  16. 16.
  17. 17. <?php $sum = 0; for ($i = 1; $i <= 5; $i++) { $sum = $sum + $i; } echo $sum; // 15
  18. 18. -module(math). -export([sum/1]). sum(Number) -> Sum = 0, Numbers = lists:seq(1, Number), lists:foreach( fun(N) -> Sum = Sum + N end, Numbers ), Sum. math:sum(5). no return keyword ** exception error: no match of right hand side value 1
  19. 19. 1> X = 5. 5 2> X. 5 3> X = X * 2. ** exception error: no match of right hand side value 10 4> 5 = 10. ** exception error: no match of right hand side value 10 but isn’t looks like assignment
  20. 20. 1> lists:sum(lists:seq(1, 5)). 15
  21. 21.
  22. 22. Imperative<?php $sum = 0; for ($i = 1; $i <= 5; $i++) { $sum = $sum + $i; } iteration keeps changing
  23. 23. -module(math2). -export([sum2/1]). sum2(Number) -> List = lists:seq(1, Number), sum2(List, 0). sum2([], Sum) -> Sum; sum2([H|T], Sum) -> sum2(T, H + Sum). 3> math2:sum(5). 15 empty list separate head from tail recurse pattern matches generate list
  24. 24. Declarative 1<?php // Declare a function! function sum($x) { if ($x == 0) { return $x; } return $x + sum($x - 1); } sum(5); // still 15 $x never changes recursion
  25. 25. Declarative 2<?php function sum2($number) { array_sum(range(1, $number)); } echo sum2(5); // yuuuup, 15 again functionfunction composition
  26. 26. Some history
  27. 27. Church Von Neumann declarative imperative
  28. 28. A lot of our code is about the hardware it runs on But programmers should worry
 about the conceptual problem domain
  29. 29. Recursion-module(math). -export([fac/1]). fac(0) -> 1; fac(N) -> N * fac(N - 1). 1> math:fac(9). 362880 function calls itself
  30. 30. Recursion fail-module(math). -export([fac/1]). fac(0) -> 1; fac(N) -> N * fac(N - 1). %%fac(9) = 9 * fac(9 - 1) %% = 9 * 8 * fac(8 - 1) %% = 9 * 8 * 7 * fac(7 - 1) %% = 9 * 8 * 7 * 6 * fac(6 - 1) %% = 9 * 8 * 7 * 6 * 5 * fac(5 -1) %% = 9 * 8 * 7 * 6 * 5 * 4 * fac(4 - 1) %% = 9 * 8 * 7 * 6 * 5 * 4 * 3 * fac(3 - 1) %% = 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * fac(2 - 1) %% = 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 * fac(1 - 1) %% = 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 * 1 %% = 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 %% = 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 %% = 9 * 8 * 7 * 6 * 5 * 4 * 6 %% = 9 * 8 * 7 * 6 * 5 * 24 %% = 9 * 8 * 7 * 6 * 120 %% = 9 * 8 * 7 * 720 %% = 9 * 8 * 5040 %% = 9 * 40320 %% = 362880 10 terms in memory
  31. 31. Tail recursion-module(math). -export([tail_fac/1]). tail_fac(N) -> tail_fac(N, 1). tail_fac(0, Acc) -> Acc; tail_fac(N, Acc) -> tail_fac(N - 1, N * Acc). tail_fac(9) = tail_fac(9, 1). tail_fac(9, 1) = tail_fac(9 - 1, 9 * 1). tail_fac(8, 9) = tail_fac(8 - 1, 8 * 9). tail_fac(7, 72) = tail_fac(7 - 1, 7 * 72). tail_fac(6, 504) = tail_fac(6 - 1, 6 * 504). tail_fac(5, 3024) = tail_fac(5 - 1, 5 * 3024). … tail_fac(0, 362880) = 362880 do calculation before recursing
  32. 32.
  33. 33. $object->setFoo(5); $value = $object->getFoo(); no return value no input value
  34. 34. $object->setFoo(5); $value = $object->getFoo(); $object->setFoo('Change of state!'); $value = $object->getFoo(); no return value no input value same argument, different return value
  35. 35. No side-effects A pure function does not rely on data outside itself does not change data outside itself
  36. 36. Object orientation Solve problems with objects Objects have internal state Modify state through methods
  37. 37. With side-effectsclass Todo { public $status = 'todo'; } function finish(Todo $task) { $task->status = 'done'; } $uhoh = new Todo(); $uhoh2 = $uhoh; finish($uhoh); echo $uhoh->status; // done echo $uhoh2->status; // done???
  38. 38. No side-effectsclass Todo { public $status = 'todo'; } function finish(Todo $task) { $copy = clone $task; $copy->status = 'done'; return $copy; } $uhoh = new Todo(); $finished = finish($uhoh); echo $finished->status; // done echo $uhoh->status; // todo cloning is cheap
  39. 39. Some state 
 must change
  40. 40. Read/write database Get user input Current date and time Random values (security)
  41. 41. Immutable objects PSR-7 HTTP messages Domain value objects DateTimeImmutable Service objects
  42. 42. Immutable infrastructure declarative configuration (Puppet, Chef) containers (Docker)
  43. 43.
  44. 44. Higher-order functions Functions are values so they can be arguments and return values
  45. 45. $names = ['Billy', 'Bob', 'Thornton']; $anonymise = anonymise('sha256'); var_dump(array_map($anonymise, $names)); // array(3) { // [0]=> // string(64) "85eea4a0285dcb11cceb68f39df10d1aa132567dec49b980345142f09f4cb05e" // [1]=> // string(64) "cd9fb1e148ccd8442e5aa74904cc73bf6fb54d1d54d333bd596aa9bb4bb4e961" // [2]=> // string(64) "d7034215823c40c12ec0c7aaff96db94a0e3d9b176f68296eb9d4ca7195c958e" // } using PHP built-ins
  46. 46. $names = ['Billy', 'Bob', 'Thornton']; $anonymise = anonymise('sha256'); var_dump(array_map($anonymise, $names)); // array(3) { // [0]=> // string(64) "85eea4a0285dcb11cceb68f39df10d1aa132567dec49b980345142f09f4cb05e" // [1]=> // string(64) "cd9fb1e148ccd8442e5aa74904cc73bf6fb54d1d54d333bd596aa9bb4bb4e961" // [2]=> // string(64) "d7034215823c40c12ec0c7aaff96db94a0e3d9b176f68296eb9d4ca7195c958e" // } function anonymise($algorithm) { return function ($value) use ($algorithm) { return hash($algorithm, $value); }; } higher-order function closure using PHP built-ins function as value function as argument
  47. 47. Middleware<?php use PsrHttpMessageRequestInterface; function add_header($header, $value) { return function (callable $handler) use ($header, $value) { return function ( RequestInterface $request, array $options ) use ($handler, $header, $value) { $request = $request->withHeader($header, $value); return $handler($request, $options); }; }; } $myStack = (new MiddlewareStack()) ->push(add_header('Silly-Header', 'and its value')); call next
 in stack immutable higher-order function
  48. 48. Middleware<?php use PsrHttpMessageServerRequestInterface as Request; use PsrHttpMessageResponseInterface as Response; $app = new SlimApp(); $app->add(function (Request $request, Response $response, callable $next) { $response->getBody()->write('Hey there, '); $response = $next($request, $response); $response->getBody()->write('up?'); return $response; }); $app->get('/', function ($request, $response, $args) { $response->getBody()->write('what’s'); return $response; }); $app->run(); // Hey there, what’s up? stream is not 
 immutable before after
  49. 49. Typehints?
  50. 50. Middleware<?php use PsrHttpMessageRequestInterface as Request; use PsrHttpMessageResponseInterface as Response; $middlewares->add($callback); class Middlewares { public function add( callable($request, Response $response, callable $next):Response ) { } } declaration of callable arguments
  51. 51. Middleware<?php use PsrHttpMessageRequestInterface as Request; use PsrHttpMessageResponseInterface as Response; $middlewares->add($callback); class Action implements Middleware { public function __invoke(Request $request, Response $response, callable $next) { // do something before $next($request, $response); // do something after } } wrap in class
  52. 52. kapolos/pramda
  53. 53. Middleware<?php $planets = [ [ "name" => "Earth", "order" => 3, "has" => ["moon", "oreos"], "contact" => [ "name" => "Bob Spongebob", "email" => "bob@spongebob.earth" ] ], [ "name" => "Mars", "order" => 4, "has" => ["aliens", "rover"], "contact" => [ "name" => "Marvin Martian", "email" => "marvin@theparanoidandroid.mars" ] ], // ... ]; $nameOfContact = P::compose(P::prop('name'), P::prop('contact')); $getContactNames = P::map($nameOfContact); // Application $contacts = $getContactNames($planets); P::toArray($contacts); //=> ["Bob Spongebob", "Marvin Martian", ...] returns a generator
  54. 54.
  55. 55. Composition over inheritance
  56. 56. Single responsibility and interface segregation
  57. 57. Service objects<?php namespace SymfonyComponentSecurityCore; interface SecurityContextInterface { public function getToken(); public function isGranted($attributes, $object = null); }
  58. 58. Service objects<?php namespace SymfonyComponentSecurityCoreAuthenticationTokenStorage; interface TokenStorageInterface { public function getToken(); } namespace SymfonyComponentSecurityCoreAuthorization; interface AuthorizationCheckerInterface { public function isGranted($attributes, $object = null); } single function
  59. 59. Single responsibility taken to its logical conclusion
  60. 60. You may not need all those patterns
  61. 61. Wrap up ➊ No assignment ➋ Declarative ➌ Side-effects
 
 ➍ Higher-order functions
 
 ➎ Patterns
  62. 62. Take-aways Reduce and isolate side-effects Create immutable value objects Be declarative
  63. 63. More?
  64. 64. https://joind.in/talk/8dfe1 @ddeboer_nl Questions?

×