KEMBAR78
Being functional in PHP (DPC 2016) | PDF
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
you’d better be ready by the time
it gets here.”
λ
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] [kernel-poll:false] [dtrace]
Eshell V7.2.1 (abort with ^G)
1>
on OS X
REPL
➊
<?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 = Sum + N
end,
Numbers
),
Sum.
math:sum(5).
no return
keyword
** exception error: no match
of right hand side value 1
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
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([H|T], Sum) -> sum2(T, H + Sum).
3> math2:sum(5).
15
empty list
separate head from tail recurse
pattern
matches
generate list
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
Declarative 2<?php
function sum2($number)
{
array_sum(range(1, $number));
}
echo sum2(5);
// yuuuup, 15 again
functionfunction
composition
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 itself
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
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
➌
$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
value
no input
value
same argument,
different return value
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 = new Todo();
$uhoh2 = $uhoh;
finish($uhoh);
echo $uhoh->status; // done
echo $uhoh2->status; // done???
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
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(3) {
// [0]=>
// string(64) "85eea4a0285dcb11cceb68f39df10d1aa132567dec49b980345142f09f4cb05e"
// [1]=>
// string(64) "cd9fb1e148ccd8442e5aa74904cc73bf6fb54d1d54d333bd596aa9bb4bb4e961"
// [2]=>
// string(64) "d7034215823c40c12ec0c7aaff96db94a0e3d9b176f68296eb9d4ca7195c958e"
// }
using PHP built-ins
$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
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
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
Typehints?
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
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
kapolos/pramda
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
➎
Composition over
inheritance
Single responsibility
and
interface segregation
Service objects<?php
namespace SymfonyComponentSecurityCore;
interface SecurityContextInterface
{
public function getToken();
public function isGranted($attributes, $object = null);
}
Service objects<?php
namespace SymfonyComponentSecurityCoreAuthenticationTokenStorage;
interface TokenStorageInterface
{
public function getToken();
}
namespace SymfonyComponentSecurityCoreAuthorization;
interface AuthorizationCheckerInterface
{
public function isGranted($attributes, $object = null);
}
single function
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)