Being functional in PHP

59
Being functional in PHP David de Boer Symfony User Group NL 24 March 2016

Transcript of Being functional in PHP

Page 1: Being functional in PHP

Being functional in PHP

David de Boer

Symfony User Group NL

24 March 2016

Page 2: Being functional in PHP

functional

Page 3: Being functional in PHP

functions

Page 4: Being functional in PHP

y = f(x)

f: X → Y

Page 5: Being functional in PHP

functional programming

functional thinking

functional communication

not languages

Page 6: Being functional in PHP

Why should you care?

Page 7: Being functional in PHP

f(x)

Others

PHPC++

C

Java

Page 8: Being functional in PHP

– 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.”

Page 9: Being functional in PHP
Page 10: Being functional in PHP

lambdas closures

2001 2009 2011 2012

array_map array_filter

callable Slim Silex

middlewaresSymfony 2.0

Page 11: Being functional in PHP

2013 2014 2015 2016

PSR-7

anonymous classes

promises futures

Page 12: Being functional in PHP

“The limits of my language mean the limits of my world.”

– Ludwig Wittgenstein

Page 14: Being functional in PHP
Page 15: Being functional in PHP
Page 16: Being functional in PHP

Erlang: The Movie

Page 17: Being functional in PHP

Erlang

Concurrent

Fault-tolerant

Functional

Page 18: Being functional in PHP

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

Page 19: Being functional in PHP

Page 20: Being functional in PHP

<?php

$sum = 0;

for ($i = 1; $i <= 5; $i++) { $sum = $sum + $i;}

echo $sum;// 15

Page 21: Being functional in PHP

-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

Page 22: Being functional in PHP

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

Page 23: Being functional in PHP

-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

Page 24: Being functional in PHP

1> lists:sum(lists:seq(1, 5)).15

Page 25: Being functional in PHP

Page 26: Being functional in PHP

Imperative

<?php

$sum = 0;for ($i = 1; $i <= 5; $i++) { $sum = $sum + $i;}

echo $sum;// 15

iteration

keeps changing

Page 27: Being functional in PHP

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

Page 28: Being functional in PHP

Declarative 2

<?php

function sum2($number){

array_sum(range(1, $number));}

echo sum2(5);// Yep, still 15

functionfunction

Page 29: Being functional in PHP

Church Von Neumann

Page 30: Being functional in PHP
Page 31: Being functional in PHP

A lot of our code is about the

hardware it runs on

But programmers should worry about the conceptual problem domain

Page 32: Being functional in PHP

Recursion

-module(math).-export([fac/1]).

fac(0) -> 1;fac(N) -> N * fac(N - 1).

1> math:fac(9).362880

Page 33: Being functional in PHP

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

Page 34: Being functional in PHP

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

Page 35: Being functional in PHP

Page 36: Being functional in PHP
Page 37: Being functional in PHP

$object->setFoo(5);

$value = $object->getFoo();

no return value

no input value

Page 38: Being functional in PHP

Time

$object->setFoo(5);

$value = $object->getFoo();

$object->setFoo('Change of state!');

$value = $object->getFoo();

no return value

no input value

Page 39: Being functional in PHP

input x → function → output y

input x → function → output y

object

input →

-

-

→ output

Page 40: Being functional in PHP

No side-effects

Does not rely on data outside itself

Does not change data outside itself

Page 41: Being functional in PHP

%% 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?

Page 42: Being functional in PHP

Object orientation

Most everything is either an object

Objects have internal state

Modify state through methods

Page 43: Being functional in PHP

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

Page 44: Being functional in PHP

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

Page 45: Being functional in PHP

Some state must change

Page 46: Being functional in PHP

Read/write database

Get user input

Current date and time

Random values (security)

Page 47: Being functional in PHP

Immutable objects

PSR-7 HTTP messages

Domain value objects

DateTimeImmutable

Service objects

Page 48: Being functional in PHP

Page 49: Being functional in PHP

Higher-order functions

Functions are values

so they can be arguments

and return values

Page 50: Being functional in PHP

<?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

Page 51: Being functional in PHP

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

Page 52: Being functional in PHP

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

Page 53: Being functional in PHP

Page 54: Being functional in PHP

Composition over

inheritance

Page 55: Being functional in PHP
Page 56: Being functional in PHP

You may not need

all those patterns

Page 57: Being functional in PHP

Take-aways

Reduce and isolate side-effects

Use value objects

Be declarative

Page 58: Being functional in PHP

More?

Page 59: Being functional in PHP

Thanks!

https://joind.in/talk/a1516

@ddeboer_nl