phpcon 2016: php7 by witek adamus / xsolve

Post on 12-Feb-2017

592 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Witek Adamus

PHPCon 2016, Rawa Mazowiecka

Why async and functional programming in PHP7 suck and how to get over it?

Who am I?

5 years of

imperative

programming

2 years of

functional

programming

Back to

PHP7

❏ What can functional programming bring to the table?

❏ When language can be described as functional?

Table of content

❏ DIY: cleaning ❏ Transparent parallelism❏ Parallelism vs Concurrency

❏ Parallel collections

Table of content

Pros and cons of functional programming

Pros Cons

Efficiency Efficiency

Entry threshold Entry threshold

Mathematical description of

reality

Mathematical description of

reality

Pros and cons of functional programming

Pros Cons In result

Efficiency Efficiency Scalability

Entry threshold Entry threshold Let’s get the party started

Mathematical description of

reality

Mathematical description of

reality

Shorter and more descriptive code

public function someMethodWithMisleadingName( array $mysteriousInput){ $arr = []; foreach ($mysteriousInput as $inp) { if ($inp > 10) { $arr[] = $inp; } } $arrLeft = $arrRight = $arrCenter = []; foreach ($arr as $elem) { $bucket = $elem <=> 20; if ($bucket < 0) { $arrLeft[] = $elem; } if ($bucket == 0) { $arrCenter[] = $elem; } if ($bucket > 0) { $arrRight[] = $elem;} } $countLeft = count($arrLeft); $countCenter = count($arrCenter); $countRight = count($arrRight); return array_sum([$countLeft, $countCenter, $countRight]) / 3;}

public function someMethodWithMisleadingName( array $mysteriousInput) { $arr = []; foreach ($mysteriousInput as $inp) { if ($inp > 10) { $arr[] = $inp; } }

$arrLeft = $arrRight = $arrCenter = []; foreach ($arr as $elem) { $bucket = $elem <=> 20; if ($bucket < 0) { $arrLeft[] = $elem; } if ($bucket == 0) { $arrCenter[] = $elem; } if ($bucket > 0) { $arrRight[] = $elem;} }

$countLeft = count($arrLeft); $countCenter = count($arrCenter); $countRight = count($arrRight);

return array_sum([$countLeft, $countCenter, $countRight]) / 3;}

public function someMethodWithMisleadingName( ParallelListCollection $mysteriousInput) { return $mysteriousInput ->filter(function ($elem) { return $elem > 10; }) ->partition(function ($elem) { return $elem <=> 20; }) ->map(function ($bucket) { return $bucket->count(); }) ->avg();}

public function someMethodWithMisleadingName( array $mysteriousInput) { $arr = []; foreach ($mysteriousInput as $inp) { if ($inp > 10) { $arr[] = $inp; } }

$arrLeft = $arrRight = $arrCenter = []; foreach ($arr as $elem) { $bucket = $elem <=> 20; if ($bucket < 0) { $arrLeft[] = $elem; } if ($bucket == 0) { $arrCenter[] = $elem; } if ($bucket > 0) { $arrRight[] = $elem;} }

$countLeft = count($arrLeft); $countCenter = count($arrCenter); $countRight = count($arrRight);

return array_sum([$countLeft, $countCenter, $countRight]) / 3;}

public function someMethodWithMisleadingName( ParallelListCollection $mysteriousInput) { return $mysteriousInput ->filter(function ($elem) { return $elem > 10; }) ->partition(function ($elem) { return $elem <=> 20; }) ->map(function ($bucket) { return $bucket->count(); }) ->avg();}

Fundamental concepts in functional programming

❏ Function is a first-class citizen

❏ No side effects

❏ Immutability

❏ Lambda Calculus

First-class citizen

❏ Can be stored in variable and data structures

❏ Can be passed as a parameter to procedure/functions

❏ Can be returned by procedures/functions

❏ Can be instantiated inline

❏ Has it’s own identity (name independent)

No side effects? Immutability?

:(

Lambda Calculus

ƛ(x) = z

Lambda Calculus

ƛ(x) = z❏ Higher-order functions❏ Currying

Functional vs Object oriented programming

?

PHP7 is functional

…but is dirty and salty as well

What do I miss in PHP7 that Scala luckily has?

❏ Immutability by default❏ Objects cloning❏ Options❏ Either❏ Future❏ Parallel collections❏ Tail recurrency

❏ Generic types❏ Arrow functions❏ Pattern matching / case classes

Few rules to make your code functional

Do not use

❏ reassignments❏ if❏ null❏ for❏ foreach

Do not use

❏ reassignments

$bigHouse = new Building(5);$smallerHouse = $bigHouse->setFloors(2);

echo $bigHouse->getFloors() . PHP_EOL; echo $smallerHouse->getFloors() . PHP_EOLl;

$bigHouse = new Building(5);$smallerHouse = $bigHouse->setFloors(2);

echo $bigHouse->getFloors() . PHP_EOL; //2echo $smallerHouse->getFloors() . PHP_EOLl; //2

class Building{ protected $floors;

public function __construct(int $floors) { $this->setFloors($floors); }

public function getFloors() : int { return $this->floors; }

public function setFloors(int $floors) : Building { $this->floors = $floors; return $this; }}

class Building{ protected $floors;

public function __construct(int $floors) { $this->floors = $floors; }

public function getFloors() : int { return $this->floors; }

private function setFloors(int $floors) : Building { $this->floors = $floors; return $this; }

public function withFloors(int $floors) : Building { return (clone($this))->setFloors($floors); }}

$bigHouse = new Building(5);$smallerHouse = $bigHouse->withFloors(2);

echo $bigHouse->getFloors() . PHP_EOL; //5echo $smallerHouse->getFloors() . PHP_EOLl; //2

trait Copy{ public function copy($field, $value) { $clone = clone $this; $clone->$field = $value; return $clone; }}

use PhpSlang\Util\Copy;

class Building{ use Copy;

protected $floors;

public function __construct(int $floors) { $this->floors = $floors; }

public function getFloors() : int { return $this->floors; }

public function withFloors(int $floors) : Building { return $this->copy('floors', $floors); }}

Few rules to make your code functional

Do not use

:) reassignments❏ if❏ null❏ for❏ foreach

https://github.com/php-slang/php-slang

Do not use

:) reassignments❏ if❏ null❏ for❏ foreach

?

Do not use

:) reassignments❏ if❏ null❏ for❏ foreach

Option

OptionMonad which may contain something or nothing

What is a monad?Option

Fishy Monad Tutorial

http://maciejpirog.github.io/fishy/ (cc-by-sa)

Fishy Monad Tutorial

http://maciejpirog.github.io/fishy/ (cc-by-sa)

Fishy Monad Tutorial

http://maciejpirog.github.io/fishy/ (cc-by-sa)

Fishy Monad Tutorial

http://maciejpirog.github.io/fishy/ (cc-by-sa)

https://github.com/php-slang/php-slang

# composer require php-slang/php-slang

Option

Option

NoneSome

Option

Option

NoneSomemap(Closure $expression)getOrElse($default)

map(Closure $expression)getOrElse($default)

Option

Option

NoneSomemap(Closure $expression)getOrElse($default)

map(Closure $expression)getOrElse($default)

Some($expression($this->content)

$this->content

Option

Option

NoneSomemap(Closure $expression)getOrElse($default)

map(Closure $expression)getOrElse($default)

None

$default

class UserRepository extends EntityRepository{ public function findByEmail(string $email) : User { $user = $this->findOneByMail(['email' => $email]);

if (!$user instanceof User) { throw new NotFoundException("oh my"); }

return $user; }}

public function findUserAction(string $email) : Response try { $user = $this ->userRepository ->findByEmail($email); return new Response($user); } catch (NotFoundException $exception) { return new Response([], Response::HTTP_NOT_FOUND); }}

class UserRepository extends EntityRepository{ public function findByEmail(string $email) : Option { return Option::of($this>findOneByMail(['email' => $email])); }}

public function findUserAction(string $email) : Response return $this ->userRepository ->findByEmail($email)); ->map(function (User $user) { return new Response($user); }) ->getOrElse(new Response('', Response::HTTP_NOT_FOUND));}

class UserRepository extends EntityRepository{ public function findByEmail(string $email) : User { $user = $this->findOneByMail(['email' => $email]);

if (!$user instanceof User) { throw new NotFoundException("oh my"); }

return $user; }}

public function findUserAction(string $email) : Response try { $user = $this ->userRepository ->findByEmail($email); return new Response($user); } catch (NotFoundException $exception) { return new Response([], Response::HTTP_NOT_FOUND); }}

class UserRepository extends EntityRepository{ public function findByEmail(string $email) : Option { return Option::of( $this>findOneByMail(['email' => $email])); }}

public function findUserAction(string $email) : Response return $this ->userRepository ->findByEmail($email)); ->map(function (User $user) { return new Response($user); }) ->getOrElse( new Response('', Response::HTTP_NOT_FOUND));}

OptionHow about nesting

public function displayNameForUser(string $email) : string{ $this ->userRepository ->findByEmail($email) ->get() ->getFirstName();}

public function displayNameForUser(string $email) : string{ $this ->userRepository ->findByEmail($email) ->map(function (User $user) { return $user->firstName; }) ->getOrElse('oh my');}

public function displayNameForUser(string $email) : ???{ $this ->userRepository ->findByEmail($email) ->map(function (User $user) { return $user->getFirstName(); });}

public function displayNameForUser(string $email) : ???{ $this ->userRepository ->findByEmail($email) ->map(function (User $user) { return $user->getFirstName(); });}

?

public function displayNameForUser(string $email) : ???{ $this ->userRepository ->findByEmail($email) ->map(function (User $user) { return $user->getFirstName(); });}

Option<Option<String>>

public function displayNameForUser(string $email) : Option{ $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); });}

public function displayNameForUser(string $email) : string{ $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); }) ->getOrElse($email);}

public function displayNameForUser(string $email) : string{ $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); }) ->getOrCall(function () use ($email) { $this->getAlternative($email); });}

public function displayNameForUser(string $email) : string{ $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); }) ->getOrCall($this->getAlternative($email));}

public function displayNameForUser(string $email) : string{ $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); }) ->getOrCall($this->getAlternative($email));}

public function getAlternative(string $email) : \Closure{ return function () use (string $email) : string { return $this->doSomethingElseWith($email); };}

Few rules to make your code functional

Do not use

:) reassignments:) if:) null❏ for❏ foreach

Shortened notation for anonymous functions

public function displayNameForUser(string $email) : string{ return $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); }) ->getOrCall($this->getAlternative($email));}

public function getAlternative(string $email) : \Closure{ return function () use (string $email) : string { return $this->doSomethingElseWith($email); };}

public function displayNameForUser(string $email) : string{ $this ->userRepository ->findByEmail($email) ->flatMap((User $user) => $user->getFirstName()) ->getOrCall(() => $this->doSomethingElseWith($email))} :(

Generic types

public function maybeSomething(string $email) : Option{ ...}

public function maybeSomething(string $email) : Option<string>{ ...}

public function maybeSomething(string $email) : Option<string>{ ...}

❏ Version: 0.4.0❏ Date: 2016-01-06❏ Author: Ben Scholzen 'DASPRiD' mail@dasprids.de, Rasmus Schultz rasmus@mindplay.dk❏ Status: Draft❏ First Published at: http://wiki.php.net/rfc/generics

:(

/*** @return Option<string>*/public function maybeSomething(string $email) : Option{ ...}

https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md

:(

What do I miss in PHP7 that Scala luckily has?

:) Immutability by default:) Objects cloning:) Options❏ Either❏ Future❏ Parallel collections❏ Tail recurrency

:( Generic types:( Arrow functions❏ Pattern matching / case classes

Either

Either

Either

RightLeft

Either

Either

RightLeftleft(Closure $expr): Eitherright(Closure $expr): Eitherget()

left(Closure $expr): Eitherright(Closure $expr): Eitherget()

public function findUserAction(string $email) : Response try { $user = $this ->userRepository ->findByEmail($email); return new Response($user); } catch (NotFoundException $exception) { return new Response([], Response::HTTP_NOT_FOUND); }}

public function findUserAction(string $email) : Response try { $user = $this ->userRepository ->findByEmail($email); return new Response($user); } catch (NotFoundException $exception) { return new Response([], Response::HTTP_NOT_FOUND); } catch (ExternalServiceResponseException $exception) { return new Response([], Response::HTTP_FAILED_DEPENDENCY); } catch (IncorrectEmailException $exception) { return new Response([], Response::HTTP_BAD_REQUEST); } catch (UserNotAllowedException $exception) { return new Response([], Response::HTTP_FORBIDDEN); }}

“Co jest piękniejszego niż rdest? No chyba tylko bylina rdestu, bo rzadziej występuje.”Ohanhutep

interface ServiceError {}

class NotFound implements ServiceError {}class ServiceNotWorking implements ServiceError {}class Param2IsPoorlyFormatted implements ServiceError {}

public function getSomethingNontrivial(string $param1, string $param2, string $param3) : Either{ ...}

Either<ServiceError, User>

public function exampleAction(string $param1, string $param2, string $param3) : Response {

return $this ->userService ->getSomethingNontrivial($param1, $param2, $param3) ->left(function (ServiceError $someKindOfFailure) : Response { new Case(ServiceNotWorking::class, new Response('', Response::HTTP_FAILED_DEPENDENCY)), new Case(Param2IsPoorlyFormatted::class, new Response('', Response::HTTP_BAD_REQUEST)), new Case(NotFound()::class, new Response([], Response::HTTP_NOT_FOUND)); }) ->right(function (User $nonTrivialAllRight) : Response { return new Response($nonTrivialAllRight, Response::HTTP_OK); }) ->get();}

public function getSomethingNontrivial( string $param1, string $param2, string $param3) : Either{ return $this ->translateParam2($param2) ->map(function ($param2Translated) { return new Right($this->getUserBy($param2Translated)); }) ->getOrElse(new Left(new Param2IsPoorlyFormatted()));}

public function getSomethingNontrivial(string $param1, string $param2, string $param3) : Either{ return $this ->translateParam2($param2) ->map($this->handleWithTranslatedParam($param1, $param3)) ->getOrElse(new Left(new Param2IsPoorlyFormatted()));}

public function handleWithTranslatedParam(string $param1, string $param3) : \Clojure{ return function (Param2Translated $param2Translated) use ($param1, $param3) : Either { return $this ->someOtherMagic($param2Translated, $param1, $param3) ->map(function (User $magicResult) use ($param1, $param3) : Either { return new Right($magicResult); }) ->getOrElse(new Left(new ServiceNotWorking())); };}

What do I miss in PHP7 that Scala luckily has?

:) Immutability by default:) Objects cloning:) Options❏ Either❏ Future❏ Parallel collections❏ Tail recurrency

:( Generic types:( Arrow functions❏ Pattern matching / case classes

Pattern Matching

public function exampleAction(string $param1, string $param2, string $param3) : Response{ return $this ->userService ->getSomethingNontrivial($param1, $param2, $param3) ->left(function (ServiceError $someKindOfFailure) : Response { return (Match::of($someKindOfFailure))->match( new Case(ServiceNotWorking::class, new Response('', Response::HTTP_FAILED_DEPENDENCY)), new Case(Param2IsPoorlyFormatted::class, new Response('', Response::HTTP_BAD_REQUEST)), new Case(NotFound::class, new Response([], Response::HTTP_NOT_FOUND)); }) ->right(function (User $nonTrivialAllRight) : Response { return new Response($nonTrivialAllRight, Response::HTTP_OK); }) ->get();}

def update(id: String): Action[JsValue] = Action.async(parse.json) { request => user.update(id, userConverterFactory.of(request.body).toInput)

.map {case Right(user) => Ok(user)case Left(UserService.UserNotFound) => NotFoundcase Left(UserService.VersioningMissmatch) => NotAcceptablecase Left(UserService.NoModificationsAllowed) => Lockedcase Left(UserService.InvalidPayload) => BadRequest

}}

What do I miss in PHP7 that Scala luckily has?

:) Immutability by default:) Objects cloning:) Options:) Either❏ Future❏ Parallel collections❏ Tail recurrency

:( Generic types:( Arrow functions:)/:( Pattern matching / case classes

Parallel collections

public function multiplyBy( array $input, float $multiplication): array{ $output = [];

foreach($input as $number) { $output[] = $number * $multiplication; }

return $output;}

use PhpSlang\Collection\ListCollection;...public function beautifulMultiplyBy( array $input, float $multiplication) : ListCollection{ return (new ListCollection($input)) ->map(function ($number) use ($multiplication) { return $number * $multiplication; });}

public function multiplyOddsBy( array $input, float $multiplication): array{ $output = [];

foreach ($input as $number) { if ($number % 2 !== 0) { $output[] = $number * $multiplication; } }

return $output;}

public function beautifulMultiplyOddsBy( array $input, float $multiplication) : ListCollection{ return (new ListCollection($input)) ->filter(function ($number) { return $number % 2 !== 0; }) ->map(function ($number) use ($multiplication) { return $number * $multiplication; });}

public function accumulatedText(array $words) : string { $text = '';

foreach ($words as $word) { $text .= $word . ' '; }

return $text;}

public function accumulatedText(array $words) : string{ return (new ListCollection($words)) ->fold('', function (string $acumulator, string $word) { return $acumulator . $word . ' '; });}

(new ListCollection([1,2,3,4]))->tail(); //ArrayCollection([2,3,4])

Few rules to make your code functional

Do not use

:) reassignments:) if:) null:) for:) foreach

Parallelism vs Concurrency

Future

public function nonBlockingGet(string $id): Future{ ...}

public function exampleAction(string $id1) : Response{ return $this ->nonBlockingService ->nonBlockingGet($id) ->map(function (NonBlockingGetResult $output) { return new Response($output); }) ->await();}

public function nonBlockingGet(string $id): Future{ ...}

public function exampleAction(string $id1) : Response{ return $this ->nonBlockingService ->nonBlockingGet($id) ->map(function (NonBlockingGetResult $output) { return new Response($output); }) ->await();}

Future<NonBlockingGetResult>

Jak powinno to wyglądać?

public function nonBlockingGet(string $id): Future{ ...}

public function exampleAction(string $id1) : Response{ return $this ->nonBlockingService ->nonBlockingGet($id) ->map(function (NonBlockingGetResult $output) { return new Response($output); }) ->await();}

Future<NonBlockingGetResult>

Jak powinno to wyglądać?

Future<Response>

public function nonBlockingGet(string $id): Future{ ...}

public function exampleAction(string $id1) : Response{ return $this ->nonBlockingService ->nonBlockingGet($id) ->map(function (NonBlockingGetResult $output) { return new Response($output); }) ->await();}

Future<NonBlockingGetResult>

Jak powinno to wyglądać?

Future<Response>

Response

public function nonBlockingGet(string $id): Future{ ...}

public function exampleAction(string $id1, string $id2, string $id3) : Response{ return Future::all([ $this->nonBlockingService1->nonBlockingGet($id1), $this->nonBlockingService2->nonBlockingGet($id2), $this->nonBlockingService3->nonBlockingGet($id3), ]) ->map(function ($output) { return new Response($output); }) ->await();}

Jak powinno to wyglądać?

public function nonBlockingGet(string $id): Future{ ...}

public function exampleAction(string $id1, string $id2, string $id3) : Response{ return Future::all([ $this->nonBlockingService1->nonBlockingGet($id1), $this->nonBlockingService2->nonBlockingGet($id2), $this->nonBlockingService3->nonBlockingGet($id3), ]) ->map(function ($output) { return new Response($output); }) ->await();}

Jak powinno to wyglądać?

Future & Parallel collections

use PhpSlang\Collection\ListCollection;...public function beautifulMultiplyBy( array $input, float $multiplication) : ListCollection{ return (new ListCollection($input)) ->map(function ($number) use ($multiplication) { return $number * $multiplication; });}

use PhpSlang\Collection\ParallelListCollection;...public function beautifulMultiplyBy( array $input, float $multiplication) : ParallelListCollection{ return (new ParallelListCollection($input)) ->map(function ($number) use ($multiplication) { return $number * $multiplication; });}

class ParallelListCollection extends ListCollection{ public function map(Closure $func) { return Future::all( (new ArrayCollection($this->elements)) ->map(function ($element) use ($func) { return new Future(function () use ($func, $element) { return $func($element); }); }) ->toArray()) ->await(); }}

class ParallelListCollection extends ListCollection{ public function map(\Closure $func) { return Future::all( (new ArrayCollection($this->elements)) ->map(function ($element) use ($func) { return new Future(function () use ($func, $element) { return $func($element); }); }) ->toArray()) ->await(); }}

ArrayCollection<Future<mixed>>

Future<ArrayCollection<mixed>>

ArrayCollection<mixed>

use PhpSlang\Collection\ParallelListCollection;...public function asyncChainedComputationExample(array $input) : ParallelListCollection{ return (new ParallelListCollection($input))

->map($this->transformationOne())

->map($this->transformationTwo())

->map($this->transformationThree();}

use PhpSlang\Collection\ParallelListCollection;...public function asyncChainedComputationExample(array $input) : ParallelListCollection{ return (new ParallelListCollection($input))

->map($this->transformationOne())

->map($this->transformationTwo())

->map($this->transformationThree();}

use PhpSlang\Collection\ParallelListCollection;...public function asyncChainedComputationExample(array $input) : ParallelListCollection{ return (new ParallelListCollection($input)) ->map(function ($number) { return Option::of($number) ->map($this->transformationOne())

->map($this->transformationTwo())

->map($this->transformationThree()

->get(); });}

use PhpSlang\Collection\ParallelListCollection;…public function asyncChainedComputationExample(array $input) : ParallelListCollection{ return (new ParallelListCollection($input)) ->map(function ($number) { return Option::of($number) ->map($this->transformationOne())

->map($this->transformationTwo())

->map($this->transformationThree()

->get(); });}

What do I miss in PHP7 that Scala luckily has?

:) Immutability by default:) Objects cloning:) Options:) Either:)/:( Future:) Parallel collections❏ Tail recurrency

:( Generic types:( Arrow functions:)/:( Pattern matching / case classes

Tail recurrency

def fibonacci(index : Int) : Int = index match { case 0 | 1 => index case _ => fibonacci(index - 1 ) + fibonacci(index - 2)}

function fibonacci(int $index) : int{ return in_array($index, [0, 1]) ? $index : fibonacci($index - 1) + fibonacci($index - 2);}

def fibonacci(index : Int) : Int = index match { case 0 | 1 => index case _ => fibonacci(index - 1 ) + fibonacci(index - 2)}

function fibonacci(int $index) : int{ return in_array($index, [0, 1]) ? $index : fibonacci($index - 1) + fibonacci($index - 2);}

echo fibonacci(123123123123);

Fatal error: Maximum function nesting level of '...' reached, aborting!

ini_set('xdebug.max_nesting_level', 9999999);

?

def fibonacci(index: Int): Int = { var a = 0 var b = 1 var i = 0

while (i < index) { val c = a + b a = b b = c i = i + 1 } return a}

function fibonacci(int $index) : int{ $a = 0; $b = 1; $i = 0;

while ($i < $index) { $c = $a + $b; $a = $b; $b = $c; $i += 1; } return $a;}

def recursiveFibonacci(n: Int, a:Int, b:Int): Int =n match { case 0 => a case _ => recursiveFibonacci( n-1, b, a+b ) }

def fibonacci( n : Int) : Int = recursiveFibonacci( n, 0, 1)

function recursiveFibonacci(int $n, int $a, int $b) { return ($n == 0) ? $a : recursiveFibonacci($n - 1, $b, $a + $b);}

function fibonacci(int $n) : int{ return recursiveFibonacci($n, 0, 1);}

def fibonacci(index : Int) : Int = index match { case 0 | 1 => index case _ => fibonacci(index - 1 ) + fibonacci(index - 2)}

function fibonacci(int $index) : int{ return in_array($index, [0, 1]) ? $index : fibonacci($index - 1) + fibonacci($index - 2);}

def recursiveFibonacci(n: Int, a:Int, b:Int): Int =n match { case 0 => a case _ => recursiveFibonacci( n-1, b, a+b ) }

def fibonacci( n : Int) : Int = recursiveFibonacci( n, 0, 1)

function recursiveFibonacci(int $n, int $a, int $b) { return ($n == 0) ? $a : recursiveFibonacci($n - 1, $b, $a + $b);}

function fibonacci(int $n) : int{ return recursiveFibonacci($n, 0, 1);} :(

Tail recurrencyTrampolines

Recurrency

Recurrency Trampoline

<?php

namespace PhpSlang\Util\Trampoline;...class Trampoline{ /** * @var TrampolineResult */ var $expression;

public function __construct(Closure $expression) { $this->expression; } public function run() { $result = new Bounce(function () { return ($this->expression)(); }); while ($result instanceof Bounce) { $result = $result->run(); } return $result->run()->get(); }}

interface TrampolineResult{ public function run() : TrampolineResult;

public function get();}

class Bounce implements TrampolineResult {...}

class Done implements TrampolineResult {...}

function recursiveFibonacci(int $n, int $a, int $b) { return ($n == 0) ? $a : recursiveFibonacci($n - 1, $b, $a + $b);}

function fibonacci($n){ return recursiveFibonacci($n, 0, 1);}

echo fibonacci(8);

function recursiveFibonacci(int $n, int $a, int $b) { return ($n == 0) ? new Done($a) : new Bounce(function () use ($n, $b, $a) { return recursiveFibonacci($n - 1, $b, $a + $b); });}

function fibonacci($n){ return (new Trampoline(function () use ($n) { return recursiveFibonacci($n, 0, 1); }))->run();}

echo fibonacci(8);

What do I miss in PHP7 that Scala luckily has?

:) Immutability by default:) Objects cloning:) Options:) Either:)/:( Future:) Parallel collections:) Tail recurrency

:( Generic types:( Arrow functions:)/:( Pattern matching / case classes

Conclusions

● Don’t be afraid of monads● Don’t set traps for your team

● Learn Haskell, Clojure, Scala, F#, JavaScript -> TypeScript

Witek Adamus

witold.adamus@xsolve.pl

xsolve.pl/nobodyexpects

top related