Friday, May 20, 2011

On Phutility

Phutility is a collection of tools which I utilize in many of my other projects. For example the Node class is utilized by both covenants (sql builder) and also phmop (meta object protocol). Today though I am going to be discussing some of the work that I have been doing in Func to lay the framework for "higher order object oriented programming" with phmop. To give you an idea of what I am driving at, consider:

defineClass('Person',
slot('someMethod', function($self) {
$args = func_get_args();
$self = array_shift($args);
$options = appos($args);
if($self->{$options[ifField]} == $options[isValue]) {
$self->call($options[callMethod], $options[withValues]);
}
})->accessor('someMethod'));

$person = new Person;
$person->someMethod(
ifField, 'age',
isValue, 30,
callMethod, 'someMethod2',
withValues, array(300));

There are a few things we can cleanup. Firstly lets create a function "method" which wraps up simple functions which utilize an accessor which is the same as their internal name. Then lets utilize a higher order function "Func\options" which returns a function which has the last param defined as an options hash to clean things up:

defineClass('Person',
method('someMethod', Func\options(function($self, $options) {
if($self->{$options[ifField]} == $options[isValue]) {
$self->call($options[callMethod], $options[withValues]);}})));

But we can go further by creation an optionsMethod which wraps up the need for the explicit Func\options call. Also we can have it pass a param which gives us back a std class for options rather than an array.

defineClass('Person',
optionsMethod('someMethod', function($self, $options) {
if($self->{$options->ifField} == $options->isValue) {
$self->call($options->callMethod, $options->withValues);}}));

For the sake of completeness let me show off another way this could be defined more explicitly taking advantage of Func\keyword which evaluates a closure and then returns a function which responds to an appositive style call:

$keywordFunc = Func\keyword(function($age, $name, $color) {
return array('age' => $age, 'name' => $name, 'color' => $color);
});

print_r($keywordFunc(
age, 30,
color, 'blue',
name, 'peter'));

array(
'age' => 30,
'name' => 'peter',
'color' => 'blue');

As you can see this allows us to pass the params in any order we please (which to be fair our Func\options implementation does as well but packaged into an $options var). So now we will skip to step 3 of what a keywordMethod would look like in use:

defineClass('Person',
keywordMethod('someMethod',
function($self, $ifField, $isValue, $callMethod, $withValues) {
if($self->$ifField == $isValue) {
$self->call($callMethod, $withValues);}}));

$person = new Person;
$person->someMethod(
ifField, 'age',
isValue, 30,
callMethod, 'someMethod2',
withValues, array(300));

I think this makes clear why both a meta object protocol and higher order functions are so awesome when they can play together. Rather than having only inheritence, interfaces, or even mixins, as the tool for making things more elegant we can easily add new features to our language with simple function composition.

No comments: