Skip to content

Instantly share code, notes, and snippets.

@mathiasverraes
Created December 9, 2014 19:40
Show Gist options
  • Save mathiasverraes/0ec97dcd2d237eb25187 to your computer and use it in GitHub Desktop.
Save mathiasverraes/0ec97dcd2d237eb25187 to your computer and use it in GitHub Desktop.
max($list, $function) in php
<?php
// test data
class Foo {
private $a;
private $b;
function __construct($a, $b)
{
$this->a = $a;
$this->b = $b;
}
public function getA() { return $this->a; }
public function getB() { return $this->b; }
}
$list = [
$foo1 = new Foo(2, "bar"),
$foo2 = new Foo(5, "baz"),
$foo3 = new Foo(1, "bam"),
];
// simple
$isGreater = function($carry, $item) {
return is_null($carry) || $item->getA() > $carry->getA() ? $item : $carry;
};
$maxOfList = array_reduce($list, $isGreater);
assert($maxOfList === $foo2);
// reusable max() function
$max = function($list, callable $extract) {
$compare = function($carry, $item) use($extract) {
return is_null($carry) || $extract($item) > $extract($carry) ? $item : $carry;
};
return array_reduce($list, $compare);
};
$extractA = function($x) { return $x->getA();};
assert($max($list, $extractA) === $foo2);
@texdc
Copy link

texdc commented Dec 10, 2014

@turanct true, it just seems like a lot of extra work/code when only basic OOP is necessary. It's also reinventing array_reduce(), in a way: both max_by() and array_reduce() would have the same signature.

@mathiasverraes
Copy link
Author

As @turanct suggests, it's perfectly possible to combine FP techniques with DDD principles. DDD is not tied to OOP at all. For example, using Lambdalicious http://verraes.net/2014/11/higher-order-programming/ you would create functions like this:

<?php
$findOldestIn = max_by( __, method(@getAge, [], __));

$oldestPupil = $findOldestIn($listOfPupils);

I think you'll agree this covers the domain + ubiquitous language part.
Ihe example that you gave is not encapsulated. Foo::maxA is a global function, that happens to reside in a class. That's not the same as encapsulation. In my example, $findOldestIn has local scope, and can only be accessed by other scopes when you explicitly hand over the function to them. That's a powerful way of information hiding.

it just seems like a lot of extra work/code when only basic OOP is necessary.

max_by only needs to defined once, somewhere globally, as it is a very generic and low level function. The definition of $findOldestIn is extremely compact. You can of course combine FP and OOP and define it inside a class:

<?php
class Pupil
{
    public static function findOldestIn($listOfPupils) {
        return max_by( $listOfPupils, method(@getAge, [], __));

(note: At the moment, max_by is not implemented in Lambdalicious, but it will sooner or later)

@texdc
Copy link

texdc commented Dec 11, 2014

Foo::maxA may not be encapsulated, but the data and logic related to Foo is. Public getters is a leaky interface, but are necessary when using global functions. Otherwise how can the logic be performed without access to the internals of an object? Therefore, a simple class method effectively encapsulates both data and logic for that particular class.

class PupilService
{
    public function findOldestIn(array $aListOfPupils)
    {
        $compareAge = function(Pupil $carry = null, Pupil $item) {
            return ($carry == null || $item->isOlderThan($carry)) ? $item : $carry;
        };
        return array_reduce($aListOfPupils, $compareAge);
    }
}

max_by(__, method(@getAge, [], __)) is not elegant - it's incoherent. Attempts to remove syntactical noise, verbosity, and ceremony should not sacrifice clarity.

edit: convert static method to service method

@texdc
Copy link

texdc commented Dec 12, 2014

Ok, in order to have a truly simple and reusable function, you want find_by, not max_by. See my fork.

class PupilService
{
    public function findOldestIn(array $aListOfPupils)
    {
        $pupilIsOlder = function(Pupil $pupilA, Pupil $pupilB) {
            return $pupilA->isOlderThan($pupilB);
        };
        return find_by($aListOfPupils, $pupilIsOlder);
    }
}

http://3v4l.org/6O94O

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment