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

Introduction à la programmation fonctionnelle en PHP - Meetup PHP Bdx 2015

Introduction à la programmation fonctionnelle en PHP - Meetup PHP Bdx 2015

La « programmation fonctionnelle » est un terme qui revient de plus en plus fréquemment dans notre industrie. Cependant, beaucoup de développeurs trouvent ce paradigme particulièrement cryptique. C’est la raison pour laquelle je vous proposerai le 24 juin une introduction aux notions utilisées dans la programmation fonctionnelle.Après avoir survolé les fondements théoriques, nous verrons ce que recouvre la programmation fonctionnelle, et comment elle peut être utilisée au sein de PHP pour améliorer la qualité de vos développements.Même si des cas d’usages liés à PHP seront présentés, les notions liées à la programmation fonctionnelle sont universelles, les autres développeurs sont donc bienvenus.

Arnaud LEMAIRE

June 24, 2015
Tweet

More Decks by Arnaud LEMAIRE

Other Decks in Programming

Transcript

  1. Fonctions d’ordre supérieur Définition function add_one() {
 return function($x) {


    return $x + 1;
 };
 }
 
 $add_one = add_one();
 $add_one(2); //return 3
  2. Généralisation function add($a, $b) {
 return $a + $b;
 }


    
 add(2, 3); //return 5 Application partielle
  3. Fonction closure Généralisation function add($a) {
 return function ($x) use

    ($a) {
 return $a + $x
 };
 }
 
 $add_one = add(1);
 $add_two = add(2);
 
 $add_one(2); //return 3
 $add_two(2); //return 4
  4. - Wikipedia « La programmation fonctionnelle est un paradigme de

    programmation qui considère le calcul en tant qu'évaluation de fonctions mathématiques. »
  5. - Wikipedia « Comme le changement d'état et la mutation

    des données ne peuvent pas être représentés par des évaluations de fonctions la programmation fonctionnelle ne les admet pas, au contraire elle met en avant l'application des fonctions, contrairement au modèle de programmation impérative qui met en avant les changements d’état »
  6. Des éléments communs, que ne possède pas PHP • High

    order function • Tail call • Passage par référence ou par valeur • Pattern matching • Types algébriques
  7. Types algébriques • type Rank = 1|2|…|valet|dame|roi • type Suit

    = Club | Diamond | Spade | Heart • type Card = Suit * Rank • type Hand = Card list
  8. Définition : conclusion • La programmation fonctionnelle est une question

    de culture • Il s’agit d’une façon différente d’aborder la résolution de problème
  9. • Quelles différences entre une variable et une fonction ?

    • Une variable représente une donnée disponible immédiatement • Une fonction représente de la donnée future
  10. • Qu’est ce que l’immutabilité ? • Toute fonction (et

    méthode) ne doit jamais modifier de la donnée
 (mais peut retourner une copie modifiée)
  11. • Qu’est ce qu’un effet de bord ? • Quand

    une fonction modifie un élément qui lui est externe
  12. • Qu’est ce qu’un objet ? • Une collection de

    fonctions qui se partagent un pointeur commun (this)
  13. Définition La suite, le retour • Structure immutable • Sans

    effet de bord • Fonctions et donnée sont considérés à équivalence
  14. Closure - Injection de dépendance Closure (et non lambda) function

    find($filter) {
 return function($data_store) use ($filter) {
 return $data_store->find_by_criteria($filter);
 };
 }
 
 $criteria = find(['name' => 'Arnaud']);
 $users = $criteria(new UserRepository);
  15. Closure - Parametrization closure function get_unpaid_by_userId($user_id) {
 return function($order_by) use

    ($user_id) {
 return 
 "SELECT FROM invoices WHERE user_id = $user_id ORDER BY $order_by";
 };
 }
 
 $unordered_invoice = get_unpaid_by_userId(1);
 
 $ordered_by_date = $unordered_invoice('issue_date');
 $ordered_by_amount = $unordered_invoice(‘amount');
  16. Composition function unpaid_invoices($invoices) {
 return array_filter($invoices,
 function ($item) {
 return

    $item->isPaid();
 });
 }
 function invoices_amount($invoices)
 {
 return array_reduce($invoices,
 function ($carry, $item) {
 return $carry + $item->getAmount();
 }, 0);
 }
 function sum_of_unpaid_invoices($invoice) {
 return invoices_amount(
 unpaid_invoices($invoice)
 );
 };
  17. Fonction d’ordre supérieur High order function function apply_discount($tickets, $discount) {


    array_map(function($ticket) use ($discount) {
 $ticket->setPrice(
 $discount($ticket->getPrice()));
 }, $tickets);
 }
  18. Application partielle function add($a) {
 return function ($x) use ($a)

    {
 return $a + $x
 };
 }
 
 $add_one = add(1);
 $add_two = add(2);
 
 $add_one(2); //return 3
 $add_two(2); //return 4
  19. Refactoring function very_complex_method($a) {
 //very complex process
 //another very complex

    process
 }
 
 function very_complex_process($a) {
 //very complex process
 }
 function another_very_complex_process($a) {
 //another very complex process 
 }
 function very_complex_method($a) {
 another_very_complex_process(
 very_complex_process($a));
 }
  20. function sum($to) {
 $sum = 0;
 for ($i = 0;

    $i<=$to; $i++) {
 $sum += $i;
 }
 return $sum;
 }
 
 function product($to) {
 $sum = 1;
 for ($i = 0; $i<=$to; $i++) {
 $sum *= $i;
 }
 return $sum;
 }
  21. $sum = function ($previous, $add) {
 return $previous + $add;


    };
 
 $product = function ($previous, $add) {
 return $previous * $add;
 }; function compute($to, $init, $action) {
 array_reduce(range(0, $to), $action, $init);
 }
 
 $sum_to = function($x) {
 return compute($x, 0, function($p, $a) {
 return $p + $a;
 });
 };
 $sum_to(3)
  22. function apply_discount($tickets) {
 foreach($tickets as $ticket) {
 $price = $ticket->getPrice();


    $ticket->setPrice($price-(0.20*$price));
 }
 
 return $tickets;
 }
  23. function apply_discount($tickets, $is_vip) {
 foreach($tickets as $ticket) {
 $price =

    $ticket->getPrice();
 if($is_vip)
 $ticket->setPrice($price-(0.33*$price));
 else
 $ticket->setPrice($price-(0.20*$price));
 }
 
 return $tickets;
 }
  24. function apply_discount($tickets, $client) {
 foreach($tickets as $ticket) {
 $price =

    $ticket->getPrice();
 if($client->isVip())
 $ticket->setPrice($price-(0.33*$price));
 else
 $ticket->setPrice($price-(0.20*$price));
 
 if($client->isFrequentBuyer()) {
 $price = $ticket->getPrice();
 $ticket->setPrice($price-5);
 }
 }
 
 return $tickets;
 }
  25. function apply_discount($tickets, $client) {
 if($client->isVip()) 
 $tickets = apply_vip_discount($tickets);
 else

    
 $tickets = apply_default_discount($tickets);
 
 if($client->isFrequentBuyer()) 
 $tickets = apply_frequentBuyer_discount($tickets);
 
 return $tickets;
 }

  26. function apply_discount($tickets, $action) {
 foreach($tickets as $ticket) {
 $price =

    $ticket->getPrice();
 $ticket->setPrice($action($price));
 }
 return $tickets;
 }
  27. function discount($ticket, $user) {
 $percentage_discount = function($x, $percentage) {
 return

    $x - ($percentage/100*$x);
 }; 
 $amount_discount = function($x, $amount) {
 return $x - $amount;
 }; 
 $vip_discount = function($x) use ($percentage_discount) {
 return $percentage_discount($x, 33);
 }; 
 $default_discount = function($x) use ($percentage_discount) {
 return $percentage_discount($x, 20);
 }; 
 $frequent_buyer_discount = function($x, $buyer_type_discount) use ($amount_discount) {
 return $amount_discount($buyer_type_discount($x), 5);
 }; 
 $frequent_buyer_discount_composable = function($buyer_type_discount) use ($frequent_buyer_discount) {
 return function($x) use ($buyer_type_discount, $frequent_buyer_discount) {
 return $frequent_buyer_discount($x, $buyer_type_discount);
 };
 }; 
 $applied_discount = ($user->isVip()) ? $vip_discount : $default_discount;
 
 return apply_discount($ticket, $frequent_buyer_discount_composable($applied_discount));
 }
  28. function compose() {
 $functions_list = array_reverse(func_get_args());
 $composed = function() use

    ($functions_list) {
 $first_function = array_shift($functions_list);
 return array_reduce(
 $functions_list,
 function ($carry, $item) {
 return $item($carry);
 },
 call_user_func_array($first_function, func_get_args())
 );
 };
 
 return $composed;
 }

  29. function available_discount_for($user) {
 $percentage_discount = function($x, $percentage) {
 return $x

    - ($percentage/100*$x);
 };
 $amount_discount = function($x, $amount) {
 return $x - $amount;
 };
 $vip_discount = function($x) use ($percentage_discount) {
 return $percentage_discount($x, 33);
 };
 $default_discount = function($x) use ($percentage_discount) {
 return $percentage_discount($x, 20);
 };
 $frequent_buyer_discount = function($x, $buyer_type_discount) use ($amount_discount) {
 return $amount_discount($buyer_type_discount($x), 5);
 };
 
 return compose($frequent_buyer_discount, 
 ($user->isVip()) ? $vip_discount : $default_discount);
 }
  30. function unpaid_invoices($invoices) {
 return array_filter($invoices,
 function ($item) {
 return $item->isPaid();


    });
 }
 function invoices_amount($invoices)
 {
 return array_reduce($invoices,
 function ($carry, $item) {
 return $carry + $item->getAmount();
 }, 0);
 }
 function sum_of_unpaid_invoices($invoice) {
 return invoices_amount(
 unpaid_invoices($invoice)
 );
 };
  31. $is_even = function($item) {
 if($item % 2 == 0) return

    true;
 return false;
 };
 
 $square = function($item) {
 return pow($item, 2);
 }; 
 array_map([1, 2, 3], $square);
 array_filter([1, 2, 3], $is_even);
  32. function map_reducer($callable, $iterable) {
 
 $map_reducer = function ($carry, $item)

    use ($callable){
 $carry[] = $callable($item);
 return $carry;
 };
 
 return array_reduce($iterable, $map_reducer, []);
 }
  33. function filter_reducer($callable, $iterable) {
 
 $filter_reducer = function ($carry, $item)

    use ($callable) {
 if($callable($item))
 $carry[] = $item;
 return $carry;
 };
 
 return array_reduce($iterable, $filter_reducer, []);
 }
  34. function make_mapper($callable) {
 return function($carry, $item) use ($callable){
 $carry[] =

    $callable($item);
 return $carry;
 };
 }
 
 array_reduce([1,2,3], make_mapper($square), []);
  35. function make_reducer($callable) {
 return function ($carry, $item) use ($callable) {


    if($callable($item))
 $carry[] = $item;
 return $carry;
 };
 }
 
 array_reduce([1,2,3], make_reducer($is_even), []);
  36. function mapping($map_callable) {
 $map_transducer = function($reducer) use ($map_callable) {
 $map_reducer

    = function($carry, $item) use ($reducer, $map_callable) {
 return $reducer($carry, $map_callable($item));
 };
 
 return $map_reducer;
 };
 
 return $map_transducer;
 }
  37. function filtering($filter_callable) {
 $filter_transducer = function($reducer) use ($filter_callable) {
 $filter_reducer

    = function($carry, $item) use ($reducer, $filter_callable) {
 if($filter_callable($item)) {
 return $reducer($carry, $item);
 } else {
 return $carry;
 }
 };
 
 return $filter_reducer;
 };
 
 return $filter_transducer;
 }