Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

PHPCon 2016: PHP7 by Witek Adamus / XSolve

787 views

Published on

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

www.xsolve.pl/nobodyexpects

Published in: Technology
  • Be the first to comment

PHPCon 2016: PHP7 by Witek Adamus / XSolve

  1. 1. Witek Adamus PHPCon 2016, Rawa Mazowiecka
  2. 2. Why async and functional programming in PHP7 suck and how to get over it?
  3. 3. Who am I?
  4. 4. 5 years of imperative programming 2 years of functional programming Back to PHP7
  5. 5. ❏ What can functional programming bring to the table? ❏ When language can be described as functional? Table of content
  6. 6. ❏ DIY: cleaning ❏ Transparent parallelism ❏ Parallelism vs Concurrency ❏ Parallel collections Table of content
  7. 7. Pros and cons of functional programming Pros Cons Efficiency Efficiency Entry threshold Entry threshold Mathematical description of reality Mathematical description of reality
  8. 8. 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
  9. 9. 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; }
  10. 10. 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(); }
  11. 11. 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(); }
  12. 12. Fundamental concepts in functional programming ❏ Function is a first-class citizen ❏ No side effects ❏ Immutability ❏ Lambda Calculus
  13. 13. 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)
  14. 14. No side effects? Immutability? :(
  15. 15. Lambda Calculus ƛ(x) = z
  16. 16. Lambda Calculus ƛ(x) = z ❏ Higher-order functions ❏ Currying
  17. 17. Functional vs Object oriented programming ?
  18. 18. PHP7 is functional …but is dirty and salty as well
  19. 19. What do I miss in PHP7 that Scala luckily has?
  20. 20. ❏ Immutability by default ❏ Objects cloning ❏ Options ❏ Either ❏ Future ❏ Parallel collections ❏ Tail recurrency ❏ Generic types ❏ Arrow functions ❏ Pattern matching / case classes
  21. 21. Few rules to make your code functional
  22. 22. Do not use ❏ reassignments ❏ if ❏ null ❏ for ❏ foreach
  23. 23. Do not use ❏ reassignments
  24. 24. $bigHouse = new Building(5); $smallerHouse = $bigHouse->setFloors(2); echo $bigHouse->getFloors() . PHP_EOL; echo $smallerHouse->getFloors() . PHP_EOLl;
  25. 25. $bigHouse = new Building(5); $smallerHouse = $bigHouse->setFloors(2); echo $bigHouse->getFloors() . PHP_EOL; //2 echo $smallerHouse->getFloors() . PHP_EOLl; //2
  26. 26. 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; } }
  27. 27. 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); } }
  28. 28. $bigHouse = new Building(5); $smallerHouse = $bigHouse->withFloors(2); echo $bigHouse->getFloors() . PHP_EOL; //5 echo $smallerHouse->getFloors() . PHP_EOLl; //2
  29. 29. trait Copy { public function copy($field, $value) { $clone = clone $this; $clone->$field = $value; return $clone; } }
  30. 30. use PhpSlangUtilCopy; 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); } }
  31. 31. Few rules to make your code functional
  32. 32. Do not use :) reassignments ❏ if ❏ null ❏ for ❏ foreach
  33. 33. https://github.com/php-slang/php-slang
  34. 34. Do not use :) reassignments ❏ if ❏ null ❏ for ❏ foreach ?
  35. 35. Do not use :) reassignments ❏ if ❏ null ❏ for ❏ foreach Option
  36. 36. Option Monad which may contain something or nothing
  37. 37. What is a monad? Option
  38. 38. Fishy Monad Tutorial http://maciejpirog.github.io/fishy/ (cc-by-sa)
  39. 39. Fishy Monad Tutorial http://maciejpirog.github.io/fishy/ (cc-by-sa)
  40. 40. Fishy Monad Tutorial http://maciejpirog.github.io/fishy/ (cc-by-sa)
  41. 41. Fishy Monad Tutorial http://maciejpirog.github.io/fishy/ (cc-by-sa)
  42. 42. https://github.com/php-slang/php-slang # composer require php-slang/php-slang
  43. 43. Option Option NoneSome
  44. 44. Option Option NoneSome map(Closure $expression) getOrElse($default) map(Closure $expression) getOrElse($default)
  45. 45. Option Option NoneSome map(Closure $expression) getOrElse($default) map(Closure $expression) getOrElse($default) Some($expression($this->content) $this->content
  46. 46. Option Option NoneSome map(Closure $expression) getOrElse($default) map(Closure $expression) getOrElse($default) None $default
  47. 47. 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); } }
  48. 48. 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)); }
  49. 49. 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)); }
  50. 50. Option How about nesting
  51. 51. public function displayNameForUser(string $email) : string { $this ->userRepository ->findByEmail($email) ->get() ->getFirstName(); }
  52. 52. public function displayNameForUser(string $email) : string { $this ->userRepository ->findByEmail($email) ->map(function (User $user) { return $user->firstName; }) ->getOrElse('oh my'); }
  53. 53. public function displayNameForUser(string $email) : ??? { $this ->userRepository ->findByEmail($email) ->map(function (User $user) { return $user->getFirstName(); }); }
  54. 54. public function displayNameForUser(string $email) : ??? { $this ->userRepository ->findByEmail($email) ->map(function (User $user) { return $user->getFirstName(); }); } ?
  55. 55. public function displayNameForUser(string $email) : ??? { $this ->userRepository ->findByEmail($email) ->map(function (User $user) { return $user->getFirstName(); }); } Option<Option<String>>
  56. 56. public function displayNameForUser(string $email) : Option { $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); }); }
  57. 57. public function displayNameForUser(string $email) : string { $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); }) ->getOrElse($email); }
  58. 58. public function displayNameForUser(string $email) : string { $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); }) ->getOrCall(function () use ($email) { $this->getAlternative($email); }); }
  59. 59. public function displayNameForUser(string $email) : string { $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); }) ->getOrCall($this->getAlternative($email)); }
  60. 60. 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); }; }
  61. 61. Few rules to make your code functional
  62. 62. Do not use :) reassignments :) if :) null ❏ for ❏ foreach
  63. 63. Shortened notation for anonymous functions
  64. 64. 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); }; }
  65. 65. public function displayNameForUser(string $email) : string { $this ->userRepository ->findByEmail($email) ->flatMap((User $user) => $user->getFirstName()) ->getOrCall(() => $this->doSomethingElseWith($email)) } :(
  66. 66. Generic types
  67. 67. public function maybeSomething(string $email) : Option { ... }
  68. 68. public function maybeSomething(string $email) : Option<string> { ... }
  69. 69. 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 :(
  70. 70. /** * @return Option<string> */ public function maybeSomething(string $email) : Option { ... } https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md :(
  71. 71. What do I miss in PHP7 that Scala luckily has?
  72. 72. :) Immutability by default :) Objects cloning :) Options ❏ Either ❏ Future ❏ Parallel collections ❏ Tail recurrency :( Generic types :( Arrow functions ❏ Pattern matching / case classes
  73. 73. Either
  74. 74. Either Either RightLeft
  75. 75. Either Either RightLeft left(Closure $expr): Either right(Closure $expr): Either get() left(Closure $expr): Either right(Closure $expr): Either get()
  76. 76. 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); } }
  77. 77. 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
  78. 78. 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>
  79. 79. 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(); }
  80. 80. 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())); }
  81. 81. 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())); }; }
  82. 82. What do I miss in PHP7 that Scala luckily has?
  83. 83. :) Immutability by default :) Objects cloning :) Options ❏ Either ❏ Future ❏ Parallel collections ❏ Tail recurrency :( Generic types :( Arrow functions ❏ Pattern matching / case classes
  84. 84. Pattern Matching
  85. 85. 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(); }
  86. 86. 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) => NotFound case Left(UserService.VersioningMissmatch) => NotAcceptable case Left(UserService.NoModificationsAllowed) => Locked case Left(UserService.InvalidPayload) => BadRequest } }
  87. 87. What do I miss in PHP7 that Scala luckily has?
  88. 88. :) Immutability by default :) Objects cloning :) Options :) Either ❏ Future ❏ Parallel collections ❏ Tail recurrency :( Generic types :( Arrow functions :)/:( Pattern matching / case classes
  89. 89. Parallel collections
  90. 90. public function multiplyBy( array $input, float $multiplication): array { $output = []; foreach($input as $number) { $output[] = $number * $multiplication; } return $output; }
  91. 91. use PhpSlangCollectionListCollection; ... public function beautifulMultiplyBy( array $input, float $multiplication) : ListCollection { return (new ListCollection($input)) ->map(function ($number) use ($multiplication) { return $number * $multiplication; }); }
  92. 92. public function multiplyOddsBy( array $input, float $multiplication): array { $output = []; foreach ($input as $number) { if ($number % 2 !== 0) { $output[] = $number * $multiplication; } } return $output; }
  93. 93. 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; }); }
  94. 94. public function accumulatedText(array $words) : string { $text = ''; foreach ($words as $word) { $text .= $word . ' '; } return $text; }
  95. 95. public function accumulatedText(array $words) : string { return (new ListCollection($words)) ->fold('', function (string $acumulator, string $word) { return $acumulator . $word . ' '; }); }
  96. 96. (new ListCollection([1,2,3,4]))->tail(); //ArrayCollection([2,3,4])
  97. 97. Few rules to make your code functional
  98. 98. Do not use :) reassignments :) if :) null :) for :) foreach
  99. 99. Parallelism vs Concurrency
  100. 100. Future
  101. 101. 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(); }
  102. 102. 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ć?
  103. 103. 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>
  104. 104. 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
  105. 105. 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ć?
  106. 106. 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ć?
  107. 107. Future & Parallel collections
  108. 108. use PhpSlangCollectionListCollection; ... public function beautifulMultiplyBy( array $input, float $multiplication) : ListCollection { return (new ListCollection($input)) ->map(function ($number) use ($multiplication) { return $number * $multiplication; }); }
  109. 109. use PhpSlangCollectionParallelListCollection; ... public function beautifulMultiplyBy( array $input, float $multiplication) : ParallelListCollection { return (new ParallelListCollection($input)) ->map(function ($number) use ($multiplication) { return $number * $multiplication; }); }
  110. 110. 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(); } }
  111. 111. 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>
  112. 112. use PhpSlangCollectionParallelListCollection; ... public function asyncChainedComputationExample(array $input) : ParallelListCollection { return (new ParallelListCollection($input)) ->map($this->transformationOne()) ->map($this->transformationTwo()) ->map($this->transformationThree(); }
  113. 113. use PhpSlangCollectionParallelListCollection; ... public function asyncChainedComputationExample(array $input) : ParallelListCollection { return (new ParallelListCollection($input)) ->map($this->transformationOne()) ->map($this->transformationTwo()) ->map($this->transformationThree(); }
  114. 114. use PhpSlangCollectionParallelListCollection; ... 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(); }); }
  115. 115. use PhpSlangCollectionParallelListCollection; … 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(); }); }
  116. 116. What do I miss in PHP7 that Scala luckily has?
  117. 117. :) Immutability by default :) Objects cloning :) Options :) Either :)/:( Future :) Parallel collections ❏ Tail recurrency :( Generic types :( Arrow functions :)/:( Pattern matching / case classes
  118. 118. Tail recurrency
  119. 119. 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); }
  120. 120. 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!
  121. 121. ini_set('xdebug.max_nesting_level', 9999999); ?
  122. 122. 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; }
  123. 123. 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); }
  124. 124. 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); }
  125. 125. 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); } :(
  126. 126. Tail recurrency Trampolines
  127. 127. Recurrency
  128. 128. Recurrency Trampoline
  129. 129. <?php namespace PhpSlangUtilTrampoline; ... 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 {...}
  130. 130. 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);
  131. 131. 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);
  132. 132. What do I miss in PHP7 that Scala luckily has?
  133. 133. :) Immutability by default :) Objects cloning :) Options :) Either :)/:( Future :) Parallel collections :) Tail recurrency :( Generic types :( Arrow functions :)/:( Pattern matching / case classes
  134. 134. Conclusions ● Don’t be afraid of monads ● Don’t set traps for your team ● Learn Haskell, Clojure, Scala, F#, JavaScript -> TypeScript
  135. 135. Witek Adamus witold.adamus@xsolve.pl
  136. 136. xsolve.pl/nobodyexpects

×