KEMBAR78
Being functional in PHP | PDF
Being functional in PHP
David de Boer
Symfony User Group NL
24 March 2016
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.”
lambdas
closures
2001 2009 2011 2012
array_map
array_filter
callable
Slim
Silex
middlewaresSymfony 2.0
2013 2014 2015 2016
PSR-7
anonymous
classes
promises
futures
“The limits of my language
mean the limits of my world.”
– Ludwig Wittgenstein
I’m David
Erlang: The Movie
Erlang
Concurrent
Fault-tolerant
Functional
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
-module(sum).
-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> sum:sum2(5).
15
empty list
separate head and tail recurse
pattern
matches
1> lists:sum(lists:seq(1, 5)).
15
➋
Imperative
<?php
$sum = 0;
for ($i = 1; $i <= 5; $i++) {
$sum = $sum + $i;
}
echo $sum;
// 15
iteration
keeps
changing
Declarative 1
<?php
// Declare a function!
function sum($x)
{
if ($x == 0) {
return $x;
}
return $x + sum($x - 1);
}
sum(5);
// still 15
$x doesn’t
change
recursion
Declarative 2
<?php
function sum2($number)
{
array_sum(range(1, $number));
}
echo sum2(5);
// Yep, still 15
functionfunction
Church Von Neumann
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
Recursion fail
-module(math).
-export([fac/11]).
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]).
fac(0) -> 1;
fac(N) -> N * fac(N - 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
➌
$object->setFoo(5);
$value = $object->getFoo();
no
return
value
no input
value
Time
$object->setFoo(5);
$value = $object->getFoo();
$object->setFoo('Change of state!');
$value = $object->getFoo();
no
return
value
no input
value
input x → function → output y
input x → function → output y
object
input →
-
-
→ output
No side-effects
Does not rely on data outside itself
Does not change data outside itself
%% records.hrl
-record(todo, {
status = reminder,
who = me,
text
}).
1> rr(records).
[todo]
2> Uhoh = #todo{status = urgent, text = "Feed cat!"}.
#todo{status = urgent,who = me,text = "Feed cat!"}
3> Done = Uhoh#todo{status = done}.
#todo{status = done,who = me,text = "Feed cat!"}
4> Uhoh.
#todo{status = urgent,who = me,text = "Feed cat!"}
still the
same
missing
objects?
Object orientation
Most everything is either an object
Objects have internal state
Modify state through methods
With side-effects
<?php
class Todo
{
public $status = 'urgent';
}
function finish(Todo $task)
{
$task->status = 'done';
}
$uhoh = new Todo();
echo $uhoh->status; // urgent
finish($uhoh);
echo $uhoh->status; // done
No side-effects
<?php
class Todo
{
public $status = 'urgent';
}
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; // urgent
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
➍
Higher-order functions
Functions are values
so they can be arguments
and return values
<?php
$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
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([
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 up');
return $response;
});
$app->run();
// Hey there, what‚ÀÙs up?
stream is not 

immutable
➎
Composition over
inheritance
You may not need
all those patterns
Take-aways
Reduce and isolate side-effects
Use value objects
Be declarative
More?
Thanks!
https://joind.in/talk/a1516
@ddeboer_nl

Being functional in PHP