Showing posts with label moose. Show all posts
Showing posts with label moose. Show all posts

Tuesday, January 19, 2010

Orlando Perl Workshop (OPW) / Perl Oasis

I had a really great time this weekend with all the guys at OPW! Although I'm obviously not a Perl guy, it was neat to see some of Perl's capabilities and some of the cool things this community is doing to improve web development. Some of these ideas could even be applied to PHP development. I'll have to give that more thought.

I was also graciously invited to speak about roles in PHP (since they were inspired by Perl's Moose::Roles module). I'll throw up another post with more details about the talk later.

There were some great speakers; I really enjoyed the talks and getting to meet fellow programmers. Stevan Little gave a great talk on everything, particularly wiring up a new app with URL routing and Bread::Board. The colorful Matt Trout told an epic poem about database management, and Marty Pauley give an interesting talk on functional programming in Perl, which actually refreshed a lot of the basics I'd forgotten from school. Shawn Moore revealed even more geek than I thought possible in his talk about conquering NetHack with an artificially intelligent bot.

Of course, there were more good things that happened there, but these are the highlights that came to mind. Many thanks to Chris Prather and his very organized and hard-working wife Jamie for their putting this shindig together. Well done! I look forward to next year. Also, I wanted to mention a particular Perl web server named Plack.

Thursday, September 24, 2009

A Different Way: Traits in PHP

For a while now I've had some friends who are Perl fanatics. Perl's OOP support is minimal, which makes it extremely flexible and powerful. One module that takes advantage of that is Moose, which provides an easy-to-use framework for OO design. Basically, Moose gives Perl the OO capabilities that most languages take for granted, if that's what you're looking for. In addtion to such capabilities are a few that most languages don't have, such as Roles.

Moose Roles are:

  • like multiple-inheritance, except that a Role handles method and attribute collisions

  • like interfaces, except that a Role is more than a contract--it's fully-working code

  • like mixins, except that Roles can have requirements for being applied to an object

  • like duck typing, except that a Role is a named collection of methods and attributes, so the state of being something is more concrete


These ideas really inspired me, but I'm stuck with PHP. Fortunately, PHP 5.3 comes with a crippled implementation of lambdas and closures, which gives just enough functionality to implement something similar to Moose::Role. I also read these two papers, which gave me some solid ideas, although I didn't read them as gospel.

You can get the code from GitHub.

How It Works

First, you have to define some traits:

TObj::Trait('Sortable',
'sort',function($obj) {
...
},
'sortAttribute','',
'setSortAttribute',function($obj,$attrName) {
$obj->sortAttribute = $attrName;
}
);

TObj::Trait('Categorizable',
'category','',
'getCategory',function($obj) {
return $obj->category;
}
);

Now we create some other class:

class Something extends TObj {
private $foo;

public function __construct($baz) {
$this->foo = $baz;
}
}

And now we apply our traits to Something object:

$something = new Something('lolwut');
$something->apply(Sortable);
$something->apply(Categorizable);

You can now call those traits' methods as if they belonged to the Something class! Note that an object's methods and attributes will override any methods or attributes in the traits, even within the context of the calling trait (see part about aliasing below). This makes traits as flexible as extended super classes.

It's a fairly simple concept, though there are some hang-ups when you get down to the nitty-gritty. For instance, what if two traits have methods or attributes with the same name? Well, you can simply exclude it from one of the traits:

$something->apply(Categorizable,array(
except=>array('getCategory')
)
);

or alias it:

$something->apply(Categorizable,array(
alias=>array('getCategory'=>'getCategoryName')
)
);

But what if getCategory() is used all over in other Categorizable methods? Now they'll actually be calling the getCategory() from the other trait, right?

Wrong! Since TObj has to handle all trait method dispatch, it does a little magic. To resolve method and attribute names, it checks for the original name ("getCategory") within the trait whose method made the call, then moves outside and checks aliased and unaliased names. It works the same way for trait attributes, too. This prevents aliases from breaking trait code.

To know if some random object you've received has a certain trait:

if ($something->applied(Categorizable)) {
echo 'It is categorizable!';
} else {
echo 'This object marches to the beat of a different drummer.';
}

To require that the object have a certain trait method:

TObj::Trait('TraitThatRequires',
required,array('aRequiredMethod'),
...
);

$something->apply(TraitThatRequires);

The last line would throw an exception because aRequiredMethod() is not yet implemented by a trait applied to $something.

TObj traits do other things, and you're welcome to check out its unit tests to see its full functionality.

This project is very new, and it has little or no documentation and I'm sure it has bugs (though all tests pass, of course). If you're interested in the project, feel free to pull it and contact me if you have any interesting ideas.

Wednesday, August 26, 2009

PHP Is a Husk

I thought there was hope. I really did. Maybe it was naivete, or simply denial because it's the language I use day-in and day-out. But there is no hope for PHP.

No, I don't think the project will die; I'm sure it will continue to grow. It's no new revelation that the language has a lot of problems. Even when we finally got prepared statements with the MySQLi interface, there were a lot of problems with the way it worked such that writing a thorough DBAL for it was nearly impossible. But lately I've been pretty positive about the direction PHP has been going--with 5.3 came namespaces, lambdas and closures, among other things.

And then I started playing around with these new features. The namespace delimiter is odd, but big deal. Lambdas and closures, though, are broken and useless for anything but the most basic applications.

For instance, I've been messing around a little with Perl and an awesome OO extension for it called Moose. Moose has a ton of features, but one particular feature is Roles. Roles, as defined by the Moose devs, are somewhere between abstract classes and mixins, with multiple inheritance--but more flexible and easier to develop with. You know, pure awesome.

So I set out to try my hand at making something similar to Roles with PHP's new lambdas and closures. That should give me all the power I need to hack something together, right? This is the best I could come up with:


// class used to enable roles for other classes
class Role {
// used by the class the role is applied to
// must be in that class's __call() method
static public function call($obj,$funcName,$args) {
$func = $obj->$funcName;
array_unshift($args,$obj);
call_user_func_array($func,$args);
}

// applies a role to the object.
// for best results, use in the constructor.
static public function apply($obj,$name) {
$mixin = new $name();
foreach ($mixin as $funcName=>$func) {
$obj->$funcName = $func;
}
}
}

// my example role that I want to apply to other classes
class MyRole {
public $yar = 'role\'s test string';

public function __construct() {

// prints a string, including a var from the object
// that is using this role.
$this->roleFunction = function($obj) {
print('This is a role function! '.$obj->blah."\n");
};

// prints the object that is using this role.
// this will also show the methods it has received
// from the role.
$this->printr = function($obj) {
print_r($obj);
};

// a function that gets overridden by Test class's
// native method by the same name.
$this->overriddenFunction = function($obj) {
print("You'll never see this.\n");
};
}
}

// my class that I want to apply a role to. Note the
// call to Role::apply() in the constructor.
class Test {
public $blah = 'test string';

public function __construct() {
Role::apply($this,'MyRole');
}

// overrides MyRole's version, as it should
public function overriddenFunction() {
print("I beat the role's method!\n");
}

// necessary mess to get $object->roleMethod() to work
public function __call($funcName,$args) {
Role::call($this,$funcName,$args);
}
}

$test = new Test();
$test->printr();


The above will print_r the Test object, as it should, including the public attributes that hold the lambdas from the Role. Neat, right?

Well, not really. This is so messy, so impractical. The limitations are ridiculous. For example, the following works:


$someFunc = function() { print('Hello, blog!'); }
$someFunc();


But this doesn't:


$obj = new StdClass();
$obj->someFunc = function() { print('Hello, blog!'); }
$obj->someFunc();


Whoops! That object method doesn't exist! And what about this:


class MyNeatoClass {
public $someVar = 'Hello, blog!';

public function __construct() {
$this->someMethod = function() { print($this->someVar); }
$this->otherMethod = function() use ($this) { print($this->someVar); }
}
}


Neither the lambda nor the closure works. $this cannot be used in lamdas or closures. So, when it comes to using these new features with objects, they're quite nearly completely useless. These are just a couple examples of what makes my above attempt at creating Roles broken and useless.

And as I mentioned before, this isn't the first time the PHP devs have botched up new features. They seem to be committed to adding "big boy" features, which should be a good thing, but not to building them correctly. This leaves developers--who actually know what these features are and how to use them in other languages--frustrated and disgusted. For what audience are they coding? Do they think Grandpa Jo's 14-year-old nephew, who "builds websites", is going to use closures? No, real developers are going to try to use them, and then give up, because they're useless.

This trend is obviously going to continue. PHP had the opportunity to recover from past mistakes with recent new features. They have not learned a thing from past mistakes. PHP, you are dead to me.