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.

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

640 views

Published on

This presentation describes basic issues related to functional programming with PHP and solution for most of problems served by the library called PhpSlang.

Published in: Engineering
  • Be the first to comment

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

  1. 1. Witek Adamus DocPlanner 2017, Warszawa
  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. ❏ WHY? ❏ What can functional programming bring to the table? ❏ WHAT? ❏ When language can be described as functional? ❏ PROBLEMS? ❏ Top sins of PHP ❏ SOLUTION? Table of content
  6. 6. WHY?
  7. 7. ❏ Higher quality? ❏ Speed / Scalability ❏ Less bugs ❏ Easy to reason about ❏ Predictability WHY?
  8. 8. WHY? Pros Cons Efficiency ??? Entry threshold ??? Mathematical description of reality ???
  9. 9. WHY? Pros Cons In result Efficiency Efficiency Scalability / Quality Entry threshold Entry threshold Let’s get the party started Mathematical description of reality Mathematical description of reality Shorter and more descriptive code
  10. 10. WHY? Pros Cons In result Efficiency Efficiency Scalability / Quality Entry threshold Entry threshold Let’s get the party started Mathematical description of reality Mathematical description of reality Shorter and more descriptive code INVEST AND WAIT FOR A DIVIDEND
  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; }
  12. 12. 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(); }
  13. 13. 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(); }
  14. 14. WHAT? ❏ Function is a first-class citizen ❏ Lambda Calculus ❏ Immutability ❏ No side effects
  15. 15. 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)
  16. 16. Lambda Calculus ƛ(x) = z
  17. 17. Lambda Calculus ƛ(x) = z ❏ Higher-order functions ❏ Currying
  18. 18. Lambda Calculus ƛ(x) = z ❏ Higher-order functions ❏ Currying SWITCH YOUR THINKING FROM “HOW?” TO “WHAT?”
  19. 19. No side effects? Immutability? :(
  20. 20. Functional vs Object oriented programming ?
  21. 21. PHP7 is functional …but is dirty and salty as well
  22. 22. What do I miss in PHP7 that Scala luckily has?
  23. 23. ❏ Immutability by default ❏ Objects cloning ❏ Options ❏ Either ❏ Future ❏ Parallel collections ❏ Tail recurrency ❏ Generic types ❏ Arrow functions ❏ Pattern matching / case classes
  24. 24. https://github.com/php-slang/php-slang http://phpslang.io # composer require php-slang/php-slang
  25. 25. Few rules to make your code functional
  26. 26. Do not use ❏ reassignments ❏ if ❏ null ❏ for ❏ foreach
  27. 27. Quick pool ❏ Do you DDD?
  28. 28. Quick pool ❏ Do you DDD? ❏ Do you use setters?
  29. 29. Quick pool ❏ Do you DDD? ❏ Do you use setters? “VALUE OBJECTS are completely immutable.” Eric Evans
  30. 30. Do not use ❏ reassignments
  31. 31. $bigHouse = new Building(5);
  32. 32. $bigHouse = new Building(5); $smallerHouse = $bigHouse->setFloors(2); echo $bigHouse->getFloors(); echo $smallerHouse->getFloors();
  33. 33. $bigHouse = new Building(5); $smallerHouse = $bigHouse->setFloors(2); echo $bigHouse->getFloors(); //2 echo $smallerHouse->getFloors(); //2
  34. 34. 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; } }
  35. 35. 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); } }
  36. 36. $bigHouse = new Building(5); $smallerHouse = $bigHouse->withFloors(2); echo $bigHouse->getFloors(); //5 echo $smallerHouse->getFloors(); //2
  37. 37. $bigHouse = new Building(5); $smallerHouse = $bigHouse->withFloors(2); echo $bigHouse->getFloors(); //5 echo $smallerHouse->getFloors(); //2 REFERENTIALTRANSPARENCY
  38. 38. Referential transparency ❏ f(x, y) = (x+y) * y
  39. 39. Referential transparency ❏ f(x, y) = (x+y) * y x, y and a result can be a function as well
  40. 40. Referential transparency ❏ f(x, y) = (x+y) * y Eg. ❏ f(2, 4) = (2 + 4) * 4 = 8 * 4 = 32 ❏ f(1, 8) = (1 + 8) * 8 = 9 * 8 = 72
  41. 41. Referential transparency ❏ f(x, y) = (x+y) * y Eg. ❏ f(2, 4) = (2 + 4) * 4 = 8 * 4 = 32 ❏ f(1, 8) = (1 + 8) * 8 = 9 * 8 = 72 i = 71; ComputingService::_opCount++; comps.push(new Tuple2(x, y));
  42. 42. Referential transparency ❏ f(x, y) = (x+y) * y Eg. ❏ f(2, 4) = (2 + 4) * 4 = 8 * 4 = 32 ❏ f(1, 8) = (1 + 8) * 8 = 9 * 8 = 72 i = 71; ComputingService::_opCount++; comps.push(new Tuple2(x, y));
  43. 43. Few rules to make your code functional
  44. 44. Do not use :) reassignments ❏ if ❏ null ❏ for ❏ foreach
  45. 45. Do not use :) reassignments ❏ if ❏ null ❏ for ❏ foreach ?
  46. 46. Do not use :) reassignments ❏ if ❏ null ❏ for ❏ foreach Option
  47. 47. Option Monad which may contain something or nothing
  48. 48. What is a monad?
  49. 49. What is a monad?
  50. 50. What is a monad?
  51. 51. What is a monad?
  52. 52. What is a monad? map
  53. 53. What is a monad? flatMap
  54. 54. What is a monad?
  55. 55. Option Option NoneSome
  56. 56. Option Option NoneSome map(Closure $expression) getOrElse($default) map(Closure $expression) getOrElse($default)
  57. 57. Option Option NoneSome map(Closure $expression) getOrElse($default) map(Closure $expression) getOrElse($default) Some($expression($this->content) $this->content
  58. 58. Option Option NoneSome map(Closure $expression) getOrElse($default) map(Closure $expression) getOrElse($default) None $default
  59. 59. public function findByEmail(string $email) : User { $user = $this->findOneBy(['email' => $email]); if (!$user instanceof User) { throw new NotFoundException("oh my"); } return $user; } try { return new Response(findByEmail($email), HTTP_OK); } catch (NotFoundException $exception) { return new Response([], HTTP_NOT_FOUND); }
  60. 60. public function findByEmail(string $email) : User { $user = $this->findOneBy(['email' => $email]); if (!$user instanceof User) { throw new NotFoundException("oh my"); } return $user; } try { return new Response(findByEmail($email), HTTP_OK); } catch (NotFoundException $exception) { return new Response([], HTTP_NOT_FOUND); } public function findByEmail(string $email) : Option { return Option::of($this->findOneBy(['email' => $email])); } return findByEmail($email)) ->map(function (User $user) { return new Response($user, HTTP_OK); }) ->getOrElse(new Response('', HTTP_NOT_FOUND));
  61. 61. public function findByEmail(string $email) : User { $user = $this->findOneBy(['email' => $email]); if (!$user instanceof User) { throw new NotFoundException("oh my"); } return $user; } try { return new Response(findByEmail($email), HTTP_OK); } catch (NotFoundException $exception) { return new Response([], HTTP_NOT_FOUND); } public function findByEmail(string $email) : Option { return Option::of($this->findOneBy(['email' => $email])); } return findByEmail($email)) ->map(function (User $user) { return new Response($user, HTTP_OK); }) ->getOrElse(new Response('', HTTP_NOT_FOUND)); map
  62. 62. Option How about nesting
  63. 63. public function findByEmail(string $email) : Option { return Option::of($this->findOneBy(['email' => $email])); } public function postalCode(string $email, PostalCode $default) : PostalCode { return findByEmail($email) ->map(function (User $user) { return $user->getProfile()->getAdress()->getPostalCode(); }) ->getOrElse($default); }
  64. 64. public function postalCode(string $email, PostalCode $default) : PostalCode { return findByEmail($email); ->map(function (User $user) { return $user->getProfile()->getAdress()->getPostalCode(); }) ->getOrElse($default); }
  65. 65. public function postalCode(string $email, PostalCode $default) : PostalCode { return findByEmail($email) ->flatMap(function (User $user) { return $user->getProfile(); }) ->flatMap(function (UserProfile $userProfile) { return $userProfile->getAdress(); }) ->flatMap(function (Adress $adress) { return $adress->getPostalCode(); }) ->getOrElse($default); }
  66. 66. public function postalCode(string $email, PostalCode $default) : PostalCode { return findByEmail($email) ->flatMap(function (User $user) { return $user->getProfile(); }) ->flatMap(function (UserProfile $userProfile) { return $userProfile->getAdress(); }) ->flatMap(function (Adress $adress) { return $adress->getPostalCode(); }) ->getOrElse($default); } flatMap
  67. 67. Few rules to make your code functional
  68. 68. Do not use :) reassignments :) if :) null ❏ for ❏ foreach
  69. 69. Shortened notation for anonymous functions
  70. 70. public function displayNameForUser(string $email) : string { return $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); }) ->getOrCall(function () use (string $email) : string { return $this->getSomethingElseWith($email); }); }
  71. 71. public function displayNameForUser(string $email) : string { return $this ->userRepository ->findByEmail($email) ->flatMap(function (User $user) { return $user->getFirstName(); }) ->getOrCall(function () use (string $email) : string { return $this->getSomethingElseWith($email); }); }
  72. 72. public displayNameForUser(string email) : string userRepository ->findByEmail(email) ->flatMap(_->getFirstName) ->getOrCall(getSomethingElseWith(email)) :(
  73. 73. public def displayNameForUser(email : string) : string = userRepository .findByEmail(email) .flatMap(_.getFirstName) .getOrElse(_ => getSomethingElseWith(email)) :)
  74. 74. public function displayNameForUser(string $email) : string { return $this ->userRepository ->findByEmail($email) ->flatMap(Extract($user)->getFirstName()) ->getOrCall(Extract($this)->getSomethingElseWith($email)); } NYI :)
  75. 75. Generic types
  76. 76. public function maybeSomething(string $email) : Option { ... }
  77. 77. /** * @return Option<string> */ public function maybeSomething(string $email) : Option { ... } https://github.com/phpDocumentor/fig-standards/blob/master/proposed/phpdoc.md :(
  78. 78. public function maybeSomething(string $email) : Option<string> { ... } :o
  79. 79. 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 :(
  80. 80. public function maybeSomething(string $email) : Option<string> { ... } WANT TO EARN EXTRA $1020 ? https://www.bountysource.com/issues/20553561-add-generics-support :~|
  81. 81. Syntax doesn’t matter thinking does Erlang guys
  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. Either
  85. 85. Either Either RightLeft
  86. 86. Either Either RightLeft left(Closure $expr): Either right(Closure $expr): Either get() left(Closure $expr): Either right(Closure $expr): Either get()
  87. 87. Either Either RightLeft left(Closure $expr): Either right(Closure $expr): Either get() left(Closure $expr): Either right(Closure $expr): Either get() SEMANTICS!
  88. 88. What do I miss in PHP7 that Scala luckily has?
  89. 89. :/ Immutability by default :) Objects cloning :) Options :) Either ❏ Future ❏ Parallel collections ❏ Tail recurrency :( Generic types :( Arrow functions ❏ Pattern matching / case classes
  90. 90. Pattern Matching
  91. 91. Pattern matching Result Variant 1 Variant 2 Variant 3 Variant N ...
  92. 92. return (Match::of($someKindResult)) ->match( new Case(IvalidInput::class, new Response('', HTTP_BAD_REQUEST)), new Case(NotFound::class, new Response('',HTTP_NOT_FOUND), new Default(function () use ($someKindResult) { return new Response($someKindResult, HTTP_OK); }) );
  93. 93. What do I miss in PHP7 that Scala luckily has?
  94. 94. :/ Immutability by default :) Objects cloning :) Options :) Either ❏ Future ❏ Parallel collections ❏ Tail recurrency :( Generic types :( Arrow functions :)/:( Pattern matching / case classes
  95. 95. Parallel collections
  96. 96. Parallel collections Collection N...3210
  97. 97. 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; }); }
  98. 98. 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 . ' '; }); }
  99. 99. (new ListCollection([1,2,3,4]))->tail(); //ListCollection([2,3,4]) (new ListCollection([1,2,3,4]))->every(2); //ListCollection([2,4]) (new ListCollection([1,2,3,4]))->groups(2); ListCollection([ ListCollection([1,2]), ListCollection([3,4]), ])
  100. 100. Few rules to make your code functional
  101. 101. Do not use :) reassignments :) if :) null :) for :) foreach
  102. 102. Parallelism vs Concurrency
  103. 103. Future
  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(); }
  105. 105. 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> NYI
  106. 106. 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> NYI Future<Response>
  107. 107. 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> NYI Future<Response> Response
  108. 108. 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(); } NYI
  109. 109. Future & Parallel collections
  110. 110. use PhpSlangCollectionListCollection; ... public function beautifulMultiplyBy(array $input, float $multiplication) : ListCollection { return (new ListCollection($input)) ->map(function ($number) use ($multiplication) { return $number * $multiplication; }); }
  111. 111. use PhpSlangCollectionParallelListCollection; ... public function beautifulMultiplyBy(array $input, float $multiplication) : ParallelListCollection { return (new ParallelListCollection($input)) ->map(function ($number) use ($multiplication) { return $number * $multiplication; }); }
  112. 112. use PhpSlangCollectionListCollection; ... public function asyncChainedComputationExample(array $input) : ListCollection { return (new ListCollection($input)) ->map($this->transformationOne()); } use PhpSlangCollectionParallelListCollection; ... public function asyncChainedComputationExample(array $input) : ParallelListCollection { return (new ParallelListCollection($input)) ->map($this->transformationOne()); }
  113. 113. use PhpSlangCollectionListCollection; ... public function asyncChainedComputationExample(array $input) : ListCollection { return (new ListCollection($input)) ->map($this->transformationOne()); } use PhpSlangCollectionParallelListCollection; ... public function asyncChainedComputationExample(array $input) : ParallelListCollection { return (new ParallelListCollection($input)) ->map($this->transformationOne()); }
  114. 114. use PhpSlangCollectionParallelListCollection; ... public function asyncChainedComputationExample(array $input) : ParallelListCollection { return (new ParallelListCollection($input)) ->map($this->transformationOne()); } CPU vs IO
  115. 115. use PhpSlangCollectionParallelListCollection; ... public function asyncChainedComputationExample(array $input) : ParallelListCollection { return (new ParallelListCollection($input)) ->map($this->transformationOne()) ->map($this->transformationTwo()) ->map($this->transformationThree(); }
  116. 116. use PhpSlangCollectionParallelListCollection; ... public function asyncChainedComputationExample(array $input) : ParallelListCollection { return (new ParallelListCollection($input)) ->map(function ($number) { return new Some($number) ->map($this->transformationOne()) ->map($this->transformationTwo()) ->map($this->transformationThree() ->get(); }); }
  117. 117. use PhpSlangCollectionParallelListCollection; … public function asyncChainedComputationExample(array $input) : ParallelListCollection { return (new ParallelListCollection($input)) ->map(function ($number) { return new Some($number) ->map($this->transformationOne()) ->map($this->transformationTwo()) ->map($this->transformationThree() ->get(); }); }
  118. 118. What do I miss in PHP7 that Scala luckily has?
  119. 119. :/ Immutability by default :) Objects cloning :) Options :) Either :/ Future :) Parallel collections ❏ Tail recurrency :( Generic types :( Arrow functions :)/:( Pattern matching / case classes
  120. 120. Tail recurrency
  121. 121. 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); }
  122. 122. 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!
  123. 123. ini_set('xdebug.max_nesting_level', 9999999); ?
  124. 124. 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; }
  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. 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); }
  127. 127. @tailrec 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); } :( :)
  128. 128. Tail recurrency Trampolines
  129. 129. Recurrency
  130. 130. Recurrency Trampoline
  131. 131. 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); }
  132. 132. 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(); }
  133. 133. Recurrency Trampoline
  134. 134. What do I miss in PHP7 that Scala luckily has?
  135. 135. :/ Immutability by default :) Objects cloning :) Options :) Either :/ Future :) Parallel collections :) Tail recurrency :( Generic types :( Arrow functions :)/:( Pattern matching / case classes
  136. 136. Conclusions ● Don’t be afraid of monads ● Care about transparency (especially referential) ● Learn Haskell, Clojure, Scala, F#, JavaScript -> TypeScript
  137. 137. Witek Adamus witold.adamus@xsolve.pl http://phpslang.io

×