Being functional in PHP
-
Upload
david-de-boer -
Category
Software
-
view
335 -
download
0
Transcript of Being functional in PHP
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
PHPC++
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.52> X.53> X = X * 2.** exception error: no match of right hand side value 104> 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 Psr\Http\Message\RequestInterface;
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 Psr\Http\Message\ServerRequestInterface as Request;use Psr\Http\Message\ResponseInterface as Response;
$app = new \Slim\App();
$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?