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

Functional approach in software design

Functional approach in software design

Functional programming is a paradigm known for decades. It is gaining popularity again, due to the rise of purely functional languages like Haskell. You may wonder, how it could be useful in PHP? Using a few techniques and a slight change of perspective you will be able to write code that is cleaner and easier to understand. You will be able to design and implement huge solutions by composing them from small and well-tested parts.

Tomasz Kowalczyk

March 08, 2018
Tweet

More Decks by Tomasz Kowalczyk

Other Decks in Programming

Transcript

  1. functional approach in
    software design
    Tomasz Kowalczyk @tmmx

    View full-size slide

  2. introduction

    View full-size slide

  3. level 0: approach

    View full-size slide

  4. paradigm shift

    View full-size slide

  5. immutability

    View full-size slide

  6. higher order functions

    View full-size slide

  7. level 1: operations

    View full-size slide

  8. casual conditionals

    View full-size slide

  9. $result = [];
    foreach($this->users as $user) {
    if($user->isDisabled()) { continue; }
    if($user->isHidden()) { continue; }
    $result[] = $user;
    }
    return $result;

    View full-size slide

  10. $result = [];
    foreach($this->users as $user) {
    if($user->isDisabled()) { continue; }
    if($user->isHidden()) { continue; }
    $result[] = $user;
    }
    return $result;

    View full-size slide

  11. $isEnabled = function(User $u): bool {
    return false === $u->isDisabled();
    };
    $isVisible = function(User $u): bool {
    return false === $u->isHidden();
    };
    return array_filter(
    array_filter($this->users, $isVisible),
    $isEnabled);

    View full-size slide

  12. $isEnabled = function(User $u): bool {
    return false === $u->isDisabled();
    };
    $isVisible = function(User $u): bool {
    return false === $u->isHidden();
    };
    return $this->users
    ->filter($isVisible)
    ->filter($isEnabled);

    View full-size slide

  13. endless loops

    View full-size slide

  14. $ids = [];
    foreach($this->users as $user) {
    foreach($user->getTags() as $tag) {
    $tagId = $tag->getId();
    if(false === in_array($tagId, $ids, true)) {
    $ids[] = $tagId;
    }
    }
    }
    return $ids;

    View full-size slide

  15. $ids = [];
    foreach($this->users as $user) {
    foreach($user->getTags() as $tag) {
    $tagId = $tag->getId();
    if(false === in_array($tagId, $ids, true)) {
    $ids[] = $tagId;
    }
    }
    }
    return $ids;

    View full-size slide

  16. $userToTags = function(User $user): array {
    return $user->getTags();
    };
    $tagToId = function(Tag $tag): int {
    return $tag->getId();
    };
    return array_unique(array_map($tagToId, array_merge(
    ...array_map($userToTags, $this->users)
    )));

    View full-size slide

  17. $userToTags = function(User $user): array {
    return $user->getTags();
    };
    $tagToId = function(Tag $tag): int {
    return $tag->getId();
    };
    return $this->users
    ->flatMap($userToTags)
    ->map($tagToId)
    ->unique();

    View full-size slide

  18. accidental accumulators

    View full-size slide

  19. $points = 0;
    foreach($this->users as $user) {
    $points += $user->getPoints();
    }
    return $points;

    View full-size slide

  20. $points = 0;
    foreach($this->users as $user) {
    $points += $user->getPoints();
    }
    return $points;

    View full-size slide

  21. $addPoints = function(int $state, User $user): int {
    return $state + $user->getPoints();
    };
    return array_reduce($this->users, $addPoints, 0);

    View full-size slide

  22. $addPoints = function(int $state, User $user): int {
    return $state + $user->getPoints();
    };
    return $this->users
    ->reduce($addPoints, 0);

    View full-size slide

  23. $isEnabled = function(User $u): bool { return !$u->isDisabled(); };
    $isVisible = function(User $user): bool { return !$u->isHidden(); };
    $userToPoints = function(User $user): int { return $user->getPoints(); };
    $addPoints = function(int $st, User $u): int { return $st + $u->getPoints(); };
    return $this->users
    ->filter($isEnabled)
    ->filter($isVisible)
    ->map($userToPoints)
    ->reduce($addPoints, 0);

    View full-size slide

  24. $isEnabled = function(User $u): bool { return !$u->isDisabled(); };
    $isVisible = function(User $user): bool { return !$u->isHidden(); };
    $userToPoints = function(User $user): int { return $user->getPoints(); };
    $addPoints = function(int $st, User $u): int { return $st + $u->getPoints(); };
    return $this->users
    ->filter($isEnabled)
    ->filter($isVisible)
    ->map($userToPoints)
    ->reduce($addPoints, 0);

    View full-size slide

  25. level 2: components

    View full-size slide

  26. request lifecycle

    View full-size slide

  27. entry | input → request
    routing | request → route
    controller | controller → response
    events | request → request
    | response → response
    forms | data → mapping
    validation | data → violations

    View full-size slide

  28. $response = controller(route($request));

    View full-size slide

  29. object graph serialization

    View full-size slide

  30. objects → normalization → formatting → data
    data → parsing → hydration → objects

    View full-size slide

  31. $json = format(normalize($user))
    $user = hydrate(parse($json))

    View full-size slide

  32. $normalizers = new NormalizerContainer();
    $normalizers->add(User::class, function(User $u) {
    return [
    'id' => $u->getId(),
    'email' => $u->getEmail(),
    ];
    });

    View full-size slide

  33. $hydrators = new HydratorContainer();
    $hydrators->add(User::class, function(array $data) {
    return new User($data['id'], $data['email']);
    });

    View full-size slide

  34. $formats = new FormatContainer();
    $formats->add('json', new JsonFormat());

    View full-size slide

  35. $s = new Serializard($normalizers, $hydrators, $formats);
    $user = new User(1337, '[email protected]');
    $json = $s->serialize($user, 'json');
    $user = $s->unserialize($json, 'json', User::class);

    View full-size slide

  36. shortcode processing

    View full-size slide

  37. This is a [b]bold text[/b].
    This is a bold text.

    View full-size slide

  38. string → parse → process → replace → string

    View full-size slide

  39. $string = replace(process(parse($input)))

    View full-size slide

  40. $handlers = new HandlerContainer();
    $handlers->add('b', function(ShortcodeInterface $s) {
    return ''.$s->getContent().'';
    });

    View full-size slide

  41. $parser = new RegexParser();
    $parser = new WordpressParser();
    $parser = new RegularParser();

    View full-size slide

  42. $processor = new Processor($parser, $handlers);
    $text = 'This is a [b]bold text[/b].';
    $result = $processor->process($text);

    View full-size slide

  43. level 3: systems

    View full-size slide

  44. modelling data flow

    View full-size slide

  45. applications
    microservices
    APIs

    View full-size slide

  46. comfort zone

    View full-size slide

  47. "real world"

    View full-size slide

  48. Slides:
    https://speakerdeck.com/thunderer/functional-approach-in-software-design
    Resources:
    https://github.com/thunderer/Shortcode
    https://github.com/thunderer/Serializard
    https://en.wikipedia.org/wiki/Functional_programming
    http://www.phptherightway.com/pages/Functional-Programming.html
    https://www.smashingmagazine.com/2014/07/dont-be-scared-of-functional-programming
    https://pragprog.com/magazines/2012-08/functional-thinking-for-the-imperative-mind
    http://lambdaconf.us/downloads/documents/lambdaconf_slfp.pdf
    Images:
    https://www.flickr.com/photos/kinematic/4078206226 (rocks)
    https://www.flickr.com/photos/kyetis/12506278675 (stones)
    https://www.flickr.com/photos/skyseeker/14404947216 (lightning)

    View full-size slide

  49. how can I help?
    github /thunderer
    twitter @tmmx

    View full-size slide

  50. please rate the talk and leave feedback
    joind.in/talk/c0b77

    View full-size slide

  51. thanks!
    github /thunderer
    twitter @tmmx

    View full-size slide