Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduction to Pragmatic Functional PHP

Introduction to Pragmatic Functional PHP

Functional programming is a paradigm with origins in lambda calculus and the 1930's. Stack Overflow's developer surveys from both 2017 and 2018 show that the languages optimized for functional programming are low in popularity. However, the same survey recognizes that developers actually working in Elixir, Clojure, F#, and Haskell, actually love their languages much more than PHP or C developers.

So functional programming is unpopular but fun!

The good news is that it can be applied in a popular language like PHP. Whilst the bad news is that PHP might not be really optimized for functional programming, we already have pretty nice libraries of functional primitives for PHP. Accepting functional programming style promises to help PHP developers develop a bit cleaner or more interesting code. More importantly, it will make it a whole lot easier to pick up a purely functional language (Hello, Haskell!) or make use of serverless architectures such as AWS Lambda.

Branislav Bujisic

October 26, 2018
Tweet

More Decks by Branislav Bujisic

Other Decks in Programming

Transcript

  1. –Saunders Mac Line, Categories for the Working Mathematician “All told,

    a monad in X is just a monoid in the category of endofunctors of X, with product X replaced by composition of endofunctors and unit set by the identity endofunctor.”
  2. Fathers of computing were playing in their laboratories... The goal:

    
 a computation model free of human decision making
  3. (λx.x)y ͢ y <?php $identity = function($x) { return $x;

    } // Expression $y applied to
 // the identity function $identity($y); // returns $y
  4. (λx.x)(λx.λy.x2+y2) <?php $identity = function($x) { return $x; } $sum_squares

    = function($x, $y) { return $x * $x + $y * $y } return $identity( $sum_squares($x, $y); ); x x2+y2 x y x
  5. EVERYTHING is a function, even a boolean value! TRUE :=

    λa.λb.a FALSE := λa.λb.b AND := λp.λq.pqp OR := λp.λq.ppq
 NOT := λp.λa.λb.pba
  6. TRUE AND FALSE = ? " TRUE FALSE TRUE "

    FALSE (λp.λq.pqp) TRUE FALSE p q " (λa.λb.a) FALSE TRUE a b
  7. EVERYTHING is a function, even a natural number! 0 :=

    λf.λx.x function zero($f, $x) { return $x;
 } 1 := λf.λx.fx function one($f, $x) { return $f($x);
 } 2 := λf.λx.f(fx) function two($f, $x) { return $f($f($x));
 }
  8. The Von Neumann Model Input Device Output Device Memory Central

    Processing Unit Control Unit Arithmetic / Logic Unit ebp-0x1 ebp-0x2 ... 00001010 00000101 ebp-0x4 ebp-0x3 mov eax, [ebp-0x1] jmp ebp-0x0 {
  9. The Machine Language mov eax, [ebp-0x1] mov ebx, [ebp-0x2] loopTop:

    cmp eax, ebx jle loopDone dec eax jmp loopTop loopDone: ebp esp FFFD ebp-0x1 ebp-0x2 ... 10 5 ebp-0x4 ebp-0x3 FFF7 ... ...
  10. Higher-Level Programming Languages 10 I = 10 20 J =

    5 30 IF I <= J GOTO 60 40 I = I - 1 50 GOTO 30 60 ... mov eax, [ebp-0x1] mov ebx, [ebp-0x2] loopTop: cmp eax, ebx jle loopDone dec eax jmp loopTop loopDone: compiler
  11. Higher-Level Programming Languages mov eax, [ebp-0x1] mov ebx, [ebp-0x2] loopTop:

    cmp eax, ebx jle loopDone dec eax jmp loopTop loopDone: for ($i = 5; $i <= 10; $i++) { ... } compiler
  12. Higher-Level Programming Languages mov eax, [ebp-0x1] mov ebx, [ebp-0x2] loopTop:

    cmp eax, ebx jle loopDone dec eax jmp loopTop loopDone: compiler MyFancyClass - i: int + myFancyLoop
  13. Once set, a variable must not be changed during the

    runtime. Immutability Basic Concepts of Functional Programming class Immutable
 {
 private $var;
 
 public function __construct($var)
 {
 $this->var = $var;
 }
 
 public function get()
 {
 return $this->var;
 }
 } Pragm atic?
  14. Not a requirement but a convenient way to prevent unplanned

    problems. Static Typing Basic Concepts of Functional Programming
  15. A pure function always returns the same result for the

    given set of parameters. sin() 3.14 0.00 1 0.84 2 0.91 Param Result 1 0.84 2 0.91 3.14 0.00 Referential Transparency Basic Concepts of Functional Programming
  16. A pure function never changes a state outside of its

    scope. Eliminated Side-Effects Basic Concepts of Functional Programming user_save() $user $user
  17. A pure function does not depend on external state and

    relies only on the parameters it was called with. No External Dependencies Basic Concepts of Functional Programming user_load($uid) $user
  18. A higher-order function can accept another functions as arguments and

    return a function. Higher-Order Functions Basic Concepts of Functional Programming array_filter($users, $callback) $callback = function ($user) { return ($user->age >= 18); }; $callback = function ($user) { return ($user->gender == 'f')
 && ($user->age < 13); };
  19. A function can be passed as argument to other functions,

    returned as value from other functions and assigned to variables. First Class Functions Basic Concepts of Functional Programming function area($r) {/*...*/} $area = function($r) {/*...*/} array_map($area, $radiuses);
  20. Testing params result [5, 0] 25 [0, 5] 3 [5,

    5] 28 [0, 0] 0 test($myObscureFunction, $table); $myFunction = function(int $a, int $b) { // I don't care about the content of
 // this function. If it is clean,
 // it will always return the same // response for the given params. }
  21. Testing function test($function, $table) { foreach ($table as $row) {

    $params = $row['params']); $expected_result = $row['result'];
 
 $result = call_user_func_array($function, $params);
 if ($result !== $expected_result) { echo "Test failed for parameters "
 . implode(', ', $row['params']); } } }
  22. Caching –Phil Carlton. Quoted by Martin Fowler in TwoHardThings "There

    are only two hard problems in Computer Science: cache invalidation and naming things.
  23. Caching Memoization params result [5, 0] 25 [0, 5] 3

    [5, 5] 28 [0, 0] 0 memoize($myObscureFunction, $parameters); $myFunction = function(int $a, int $b) { // I don't care about the content of
 // this function. If it is clean,
 // it will always return the same // response for the given params. }
  24. Memoization:
 Nuts and Bolts function memoize(callable $func, $params = [])

    { return call_user_func_array($func, $params); } 
 $result = memoize($somethingTerriblyExpensive, [2, 5]); $somethingTerriblyExpensive = function($a, $b) { // ...
 } 
 static $storage = []; $key = get_class($func) . implode(':', $params); // OMG, BAD!! if (!isset($storage[$key])) { $storage[$key] = call_user_func_array($func, $params); } return $storage[$key];
  25. –Neal Ford. Functional Thinking (Sebastopol: O'Reilly, 2014) "I’d rather spend

    my time at a higher level of abstraction, thinking about ways to solve complex business scenarios, not complicated plumbing problems."
  26. Higher level of abstraction means: • Less plumbing problems •

    More focus on business problems • Easier debugging and refactoring • Less control over every aspect of the program • Performance tradeoffs
  27. The Stack Push Pop FFFD FFFE FFFF FFFC FFFB FFFA

    Addr Value FFF8 FFF9 FFF7 FFF6 Stack Frame ebp esp Junk Junk
  28. Loops are not business logic $output = [];
 foreach ($collection

    as $item) { $output[] = strtoupper($item); }; process
 $collection[0] process
 $collection[1] process
 $collection[2] process
 $collection[3] Sequential Execution Guaranteed™
  29. The loop still exists, but it's a responsibility of compiler

    $output = array_map(function($item) { strtoupper($item); }, $collection); process
 $collection[0] process
 $collection[1] process
 $collection[2] process
 $collection[3]
  30. Process could be parallelized $output = array_map(function($item) { strtoupper($item); },

    $collection); process
 $collection[0] process
 $collection[1] process
 $collection[2] process
 $collection[3] assemble result parallelize
 job
  31. Process could be parallelized $output = array_map(function($item) { strtoupper($item); },

    $collection); process
 $collection[0] process
 $collection[1] process
 $collection[2] process
 $collection[3] assemble result parallelize
 job
  32. This "might" work with PHP • https://github.com/krakjoe/pthreads
 Threading for PHP

    • PHP 7.2+ • ZTS Enabled (Thread Safety) • Posix Threads Implementation • Some boilerplate work and/or 
 https://github.com/functional-php/parallel
  33. Decorator Pattern Pizza 4.00 Mushrooms +1.00 Mozarella +2.00 Basil +0.50

    Mushrooms getPrice() <i> Sellable getPrice() Pizza getPrice() <a> Topping __construct(Sellable)
 getPrice() Mozarella getPrice() Basil getPrice()
  34. Decorator Pattern $pizza = new Pizza(); $mushroomPizza = new Mushrooms($pizza);

    $mushroomMozarellaPizza = new Mozarella($mushroomPizza); //... $mushroomMozarellaBasilPizza ->getPrice(); Mushrooms getPrice() <i> Sellable getPrice() Pizza getPrice() <a> Topping __construct(Sellable)
 getPrice() Mozarella getPrice() Basil getPrice() $badPizza = new Basil( new Mozarella( new Mushrooms ( new Pizza() ) ) );
  35. Functional solution is called composition $badPizza = compose($mushrooms, $mozarella, $basil);

    $mushrooms = function($price) {return $price + 1;};
 $mozarella = function($price) {return $price + 2;};
 $basil = function($price) {return $price + .5;}; $badPizzaPrice = $badPizza(4); // 4 == base price // Tripple Mozarella FTW
 $fixBadPizza = compose($badPizza, $mozarella, $mozarella);
  36. It's not all lollipops and unicorns function identity($value) { return

    $value; } function compose(...$functions) { return array_reduce( $functions, function ($chain, $function) { return function ($input) use ($chain, $function) { return $function($chain($input)); }; }, 'identity' ); }
  37. Operations on traversable objects and arrays use function Functional\every; use

    function Functional\select; use function Functional\invoke;
 
 if (every($group, function($user) {$user->age > 18}) {
 print "You may enter the bar!"; } $kids = select($group, function($user) {
 $user->age < 12;
 }); invoke($kids, 'goToBed', []); // ...
  38. time 0s 0.4s 0.8s 1.2s 1.6s map filter reduce 0.86

    1.54 1.27 0.48 1.12 0.72 0.23 0.54 0.58 imperative functional lstrojny/functional-php Tested on OS X, Apache 2.4 and PHP 7.1 10 000 000 operations
  39. Synchronous monthly invoicing Payment Pending Order Email the user Charge

    Credit Card Completed Order 1 000 times in a single night,
 1 minute per job (optimistic)
 Total: 16h 40m Issue the invoice
  40. Synchronous monthly invoicing Payment Pending Order Email the user Charge

    Credit Card Completed Order 1 000 times in a single night,
 1 minute per job (optimistic)
 Total: 16h 40m Issue the invoice Invoicing Queue
  41. Synchronous monthly invoicing Payment Pending Order Email the user Charge

    Credit Card Completed Order 1 000 times in a single night,
 1 minute per job (optimistic)
 Total: 16h 40m Invoicing Queue Issue the invoice Issue the invoice Issue the invoice
  42. Serverless • Compute Service On Demand • Pay infrastructure only

    during the execution of functions • AWS Lambda (Java, Python, Node.js, C#)
 Google Cloud Functions (Node.js)
 MS Azure Functions (C#, Node.js)
 IBM Cloud Functions (Node.js, Swift, Java, PHP, Python, any compiled language in Docker) • Execute functions on event or on http request
  43. Thanks! Wanna talk about functional programming, platform.sh or even monads?


    
 Come by the Platform.sh booth or drop me an email! Branislav Bujisic
 [email protected] @bbujisic