Being functional in PHP (DPC 2016)

71
Being functional in PHP David de Boer

Transcript of Being functional in PHP (DPC 2016)

Page 1: Being functional in PHP (DPC 2016)

Being functional in PHP

David de Boer

Page 2: Being functional in PHP (DPC 2016)
Page 3: Being functional in PHP (DPC 2016)

functional

Page 4: Being functional in PHP (DPC 2016)

functions

Page 5: Being functional in PHP (DPC 2016)

y = f(x)

f: X → Y

Page 6: Being functional in PHP (DPC 2016)

functional programming

functional thinking

functional communication

not languages

Page 7: Being functional in PHP (DPC 2016)

Why should you care?

Page 8: Being functional in PHP (DPC 2016)
Page 9: Being functional in PHP (DPC 2016)
Page 10: Being functional in PHP (DPC 2016)

f(x)

Others

PHPC++

C

Java

Page 11: Being functional in PHP (DPC 2016)

– 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 12: Being functional in PHP (DPC 2016)

λ

closures

__invoke

2001 2009 2011 2012

array_map

array_filter

callable

Slim

Silex

middlewares Symfony2

Page 13: Being functional in PHP (DPC 2016)

2013 2014 2015 2016

PSR-7

anonymous classes

foreach

promises

futuresStackPHP

HTTP middleware

proposal

Page 14: Being functional in PHP (DPC 2016)

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

– Ludwig Wittgenstein

Page 16: Being functional in PHP (DPC 2016)
Page 17: Being functional in PHP (DPC 2016)

Erlang: The Movie

Page 18: Being functional in PHP (DPC 2016)

Erlang

Concurrent

Passing messages

Fault-tolerant

Page 19: Being functional in PHP (DPC 2016)

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 20: Being functional in PHP (DPC 2016)

Page 21: Being functional in PHP (DPC 2016)

<?php

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

echo $sum;// 15

Page 22: Being functional in PHP (DPC 2016)

-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 23: Being functional in PHP (DPC 2016)

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 24: Being functional in PHP (DPC 2016)

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

Page 25: Being functional in PHP (DPC 2016)

Page 26: Being functional in PHP (DPC 2016)

Imperative<?php

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

iteration

keeps changing

Page 27: Being functional in PHP (DPC 2016)

-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

Page 28: Being functional in PHP (DPC 2016)

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

Page 29: Being functional in PHP (DPC 2016)

Declarative 2<?php

function sum2($number){

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

echo sum2(5);// yuuuup, 15 again

functionfunction

composition

Page 30: Being functional in PHP (DPC 2016)

Some history

Page 31: Being functional in PHP (DPC 2016)

Church Von Neumann

declarative imperative

Page 32: Being functional in PHP (DPC 2016)
Page 33: Being functional in PHP (DPC 2016)

A lot of our code is about the

hardware it runs on

But programmers should worry about the conceptual problem domain

Page 34: Being functional in PHP (DPC 2016)

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

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

1> math:fac(9).362880

function calls itself

Page 35: Being functional in PHP (DPC 2016)

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

Page 36: Being functional in PHP (DPC 2016)

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

Page 37: Being functional in PHP (DPC 2016)

Page 38: Being functional in PHP (DPC 2016)
Page 39: Being functional in PHP (DPC 2016)

$object->setFoo(5);

$value = $object->getFoo();

no return value

no input value

Page 40: Being functional in PHP (DPC 2016)

$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

Page 41: Being functional in PHP (DPC 2016)

No side-effects

A pure function

does not rely on data outside itself

does not change data outside itself

Page 42: Being functional in PHP (DPC 2016)

Object orientation

Solve problems with objects

Objects have internal state

Modify state through methods

Page 43: Being functional in PHP (DPC 2016)

With side-effectsclass Todo{

public $status = 'todo';}

function finish(Todo $task){

$task->status = 'done';}

$uhoh = new Todo();$uhoh2 = $uhoh;

finish($uhoh);

echo $uhoh->status; // doneecho $uhoh2->status; // done???

Page 44: Being functional in PHP (DPC 2016)

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; // doneecho $uhoh->status; // todo

cloning is cheap

Page 45: Being functional in PHP (DPC 2016)

Some state must change

Page 46: Being functional in PHP (DPC 2016)

Read/write database

Get user input

Current date and time

Random values (security)

Page 47: Being functional in PHP (DPC 2016)

Immutable objects

PSR-7 HTTP messages

Domain value objects

DateTimeImmutable

Service objects

Page 48: Being functional in PHP (DPC 2016)

Immutable infrastructure

declarative configuration (Puppet, Chef)

containers (Docker)

Page 49: Being functional in PHP (DPC 2016)

Page 50: Being functional in PHP (DPC 2016)

Higher-order functions

Functions are values

so they can be arguments

and return values

Page 51: Being functional in PHP (DPC 2016)

$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

Page 52: Being functional in PHP (DPC 2016)

$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

Page 53: Being functional in PHP (DPC 2016)

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())->push(add_header('Silly-Header', 'and its value'));

call next in stack

immutable

higher-order function

Page 54: Being functional in PHP (DPC 2016)

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');

return $response;});

$app->run();// Hey there, what’s up?

stream is not immutable

before

after

Page 55: Being functional in PHP (DPC 2016)

Typehints?

Page 56: Being functional in PHP (DPC 2016)

Middleware<?php

use Psr\Http\Message\RequestInterface as Request;use Psr\Http\Message\ResponseInterface as Response;

$middlewares->add($callback);

class Middlewares{ public function add( callable($request, Response $response, callable $next):Response ) {

}

}

declaration of callable arguments

Page 57: Being functional in PHP (DPC 2016)

Middleware<?php

use Psr\Http\Message\RequestInterface as Request;use Psr\Http\Message\ResponseInterface 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

Page 58: Being functional in PHP (DPC 2016)

kapolos/pramda

Page 59: Being functional in PHP (DPC 2016)

Middleware<?php

$planets = [ [ "name" => "Earth", "order" => 3, "has" => ["moon", "oreos"], "contact" => [ "name" => "Bob Spongebob", "email" => "[email protected]" ] ], [ "name" => "Mars", "order" => 4, "has" => ["aliens", "rover"], "contact" => [ "name" => "Marvin Martian", "email" => "[email protected]" ] ], // ...];

$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

Page 60: Being functional in PHP (DPC 2016)

Page 61: Being functional in PHP (DPC 2016)

Composition over

inheritance

Page 62: Being functional in PHP (DPC 2016)
Page 63: Being functional in PHP (DPC 2016)

Single responsibility

and

interface segregation

Page 64: Being functional in PHP (DPC 2016)

Service objects<?php

namespace Symfony\Component\Security\Core;

interface SecurityContextInterface{ public function getToken(); public function isGranted($attributes, $object = null);}

Page 65: Being functional in PHP (DPC 2016)

Service objects<?php

namespace Symfony\Component\Security\Core\Authentication\Token\Storage;

interface TokenStorageInterface{ public function getToken();}

namespace Symfony\Component\Security\Core\Authorization;

interface AuthorizationCheckerInterface{ public function isGranted($attributes, $object = null);}

single function

Page 66: Being functional in PHP (DPC 2016)

Single responsibility

taken to its

logical conclusion

Page 67: Being functional in PHP (DPC 2016)

You may not need

all those patterns

Page 68: Being functional in PHP (DPC 2016)

Wrap up

➊ No assignment

➋ Declarative

➌ Side-effects ➍ Higher-order functions➎ Patterns

Page 69: Being functional in PHP (DPC 2016)

Take-aways

Reduce and isolate side-effects

Create immutable value objects

Be declarative

Page 70: Being functional in PHP (DPC 2016)

More?

Page 71: Being functional in PHP (DPC 2016)

https://joind.in/talk/8dfe1

@ddeboer_nl

Questions?