functional programming with php7
TRANSCRIPT
Functional Programming with PHP 7
What we will learn?
Functional Programming concepts, jargons and a lot of beautiful examples
that will be blow your mind.
And where I can use it? Can I apply it on a daily
basis?
In your data, business logic, etc… Of course!
https://speakerdeck.com/marcelgsantos/programacao-funcional-
em-php-saia-da-zona-de-conforto
For a deeper knowledge
Let’s go to the party!
Lambda or Anonymous function
$foo = function () {};
Closure
$bar = function () use ($fuzz) {}
It has access to external scope
Higher-order functions
Takes one or more functions as arguments and returns a function as its result.
Currying
Currying is converting a single function of n arguments into n functions with a single
argument each.
f(x, y, z) -> f(x)(y)(z)
function has($x){ return function (array $xss) use ($x) { return isset($xss[$x]); };}
$hasName = has('name');$hasName(['name' => 'Sérgio']); //=> true$hasName(['yearsOld' => 26]); //=> false
has('lastName')(['lastName' => 'Siqueira']); //=> true
Currying vs Partial Application
Partial Application, what?
function has(...$args){ return partial(function (array $xss, $x) { return isset($xss[$x]); })(...$args);}
$hasName = has('name');$hasName(['name' => 'Sérgio']); //=> true$hasName(['yearsOld' => 26]); //=> false
has('lastName')(['lastName' => 'Siqueira']); //=> true
function has(...$args){ return partial(function (array $xss, $x) { return isset($xss[$x]); })(...$args);}
$hasName = has('name');$hasName(['name' => 'Sérgio']); //=> true$hasName(['yearsOld' => 26]); //=> false
has('lastName')(['lastName' => 'Siqueira']); //=> true
Tacit programming or point-free style
function has(...$args){ return partial(function (array $xss, $x) { return isset($xss[$x]); })(...$args);}
$hasName = has('name');$hasName(['name' => 'Sérgio']); //=> true$hasName(['yearsOld' => 26]); //=> false
has('lastName')(['lastName' => 'Siqueira']); //=> true
function partial(callable $fn, ...$args){ $arity = (new \ReflectionFunction($fn)) ->getNumberOfRequiredParameters();
return isset($args[$arity - 1]) ? $fn(...$args) : function (...$restArgs) use ($fn, $args) { return partial($fn, ...array_merge($args, $restArgs)); };}
function partial(callable $fn, ...$args){ $arity = (new \ReflectionFunction($fn)) ->getNumberOfRequiredParameters();
return isset($args[$arity - 1]) ? $fn(...$args) : function (...$restArgs) use ($fn, $args) { return partial($fn, ...array_merge($args, $restArgs)); };}
Arity: Number of arguments a function or operator takes.
function partial(callable $fn, ...$args){ $arity = (new \ReflectionFunction($fn)) ->getNumberOfRequiredParameters();
return isset($args[$arity - 1]) ? $fn(...$args) : function (...$restArgs) use ($fn, $args) { return partial($fn, ...array_merge($args, $restArgs)); };}
function partial(callable $fn, ...$args){ $arity = (new \ReflectionFunction($fn)) ->getNumberOfRequiredParameters();
return isset($args[$arity - 1]) ? $fn(...$args) : function (...$restArgs) use ($fn, $args) { return partial($fn, ...array_merge($args, $restArgs)); };}
Stop! Where I use it?
Okey! Let’s go to my prelude!
Remember, in PHP, array also is hashmap
public function validate($value, Constraint $constraint){ if (empty($value)) { return $value; }
$violation = function () use ($value, $constraint) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $value) ->addViolation(); };
return pipe( function ($name) { return preg_replace('/(\d|[^\s\w])/u', '', $name); }, function ($name) { return preg_replace('/[\n\t\r]/', ' ', $name);
function tail(array $xss){ return array_slice($xss, 1);}
function ifElse(callable $pred){ return function (callable $succfn) use ($pred) { return function (callable $failfn) use ($pred, $succfn) { return function ($x = null) use ($pred, $succfn, $failfn) { return $pred($x) ? $succfn($x) : $failfn($x); }; }; };}
function id($id){ return $id;}
function lt($x){ return function ($y) use ($x) { return $x < $y; };}
function pipe(callable ...$callbacks){ return function ($payload) use ($callbacks) { $rest = tail(func_get_args());
return array_reduce($callbacks, function ($payload, $callback) use ($rest) { return $callback(...array_merge([$payload], $rest)); }, $payload); };}
public function validate($value, Constraint $constraint){ if (empty($value)) { return $value; }
$violation = function () use ($value, $constraint) { $this->context->buildViolation($constraint->message) ->setParameter('{{ value }}', $value) ->addViolation(); };
return pipe( function ($name) { return preg_replace('/(\d|[^\s\w])/u', '', $name); }, function ($name) { return preg_replace('/[\n\t\r]/', ' ', $name); }, function ($name) { return preg_replace('/\s(?=\s)/', ' ', $name); }, function ($name) { return preg_match_all('/\w{2,}/u', trim($name)); }, ifElse(lt(1))(id)($violation) )($value);}
Immutability
$validate = anyPass([ [Validator::phone(), 'validate'], [Validator::cpf(), 'validate'],]);$sanitize = ifElse($validate) (function ($subject) { return preg_replace('/\D+/', '', $subject); }) (id);
$user = $this->createQueryBuilder('u') ->where('u.email = :username') ->orWhere("JSONB_HGG(u.data, '{phones, mobile}') = :username") ->orWhere("JSONB_MGG(u.data, 'cpf') = :username") ->setParameter('username', $sanitize($username)) ->getQuery() ->getOneOrNullResult();
$throw = function () { throw new \UserNotFoundException();};
return $user === null ? $throw() : $user;
function anyPass(array $preds){ return function ($x) use ($preds) { return array_reduce($preds, function (bool $prev, callable $pred) use ($x) { return true === $prev ? $prev : $pred($x); }, false ); };}
$foo = anyPass([ function (array $x) { return isset($x['mobile']); }, function (array $x) { return isset($x['last_name']); }]);
$foo(['last_name' => 'Siqueira']); //=> true$foo(['lastName'] => 'Siqueira'); //=> false
Lack the last argument,
point-free style
$foo = anyPass([ has('mobile'), has(‘last_name')]);
$foo(['last_name' => 'Siqueira']); //=> true$foo(['lastName'] => 'Siqueira'); //=> false
public function getOwner(): UserInterface{ $data = pipe(filter(has('owner')), head, get) ($this->data['admins'] ?? []);
return new User( $data('id'), $data('name'), new Email($data('email')) );}
http://github.com/sergiors/prelude
Thank you! 🍻Twitter @serg1ors Github @sergiors http://sergiors.com