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.

Sunday, August 23, 2009

Chaos vs. Best Practice: Let Them Eat Cake

Even if you didn't know who _why was a couple weeks ago, most likely you've gathered enough since then to know that he was a very bright and artistic programmer (or a very bright and logical artist?) who inexplicably erased his digital presence.

But enough about _why, as that dead horse has been and is currently being beaten to horse pudding. However, in looking through some of _why's archives (things are rarely truly erased), I found this insightful and interesting blog post.

To sum up, _why feels like so called "best practices" are dead, boring, and stifling. Good, artistic and revolutionary code comes from chaos. Diagrams and TDD are simply scripted banality. I see his point, but I responded to his post thus:


Refusing to be stifled by best practices is one thing, and it’s not a new thing; to touch on the gagging cliches, everyone who’s ever caused a shift in perception or the field of possibilities--Beethoven, Lennon, Newton, DaVinci--questioned the “best practices” of their field in their day. But you mentioned yourself that we can’t possibly understand every avenue, every corner, of the systems we work with. Isn’t that why we have best practices? To invent the wheel without understanding friction coefficients is foolish. Even though you disagree with their choice, maybe your predecessors decided a sleigh made more sense because you didn’t realize that you live in North Dakota.

Chaos is for after-hours. Chaos is a bunch of possibilities and fewer probabilities, with only one or two sure things. Once you’ve polished all that and have one sure thing in your hands, use it at your day job. At that point, it’s a best practice. Best practices have an origin. Chaos has a conclusion; to claim to subscribe to one and reject the other is short-sighted. To accept both as part of the process shows your passion and dedication to your field.


To expand on this response, there are three types of programmers with respect to chaos and best practice:

  • Great programmers who can dabble in chaos, confident that a best practice will rise from the ashes of many smart--but failed--attempts. They then share and use these practices. These are the French elite of programmers.

  • Decent programmers who dabble in chaos but do not develop best practices. Such dabbling helps them to see the wisdom in best practices and use them wisely to make solid software. These are the French bourgeoisie.

  • Programmers who shouldn't be programmers--the peasant farmers.


I appreciate _why's contributions to programming-as-art. And I really believe that he is brilliant enough to develop best practices from chaos. But his advice to abandon best practice for chaos is saying to the rest of us, "Let them eat cake!" I'm sure his elitism is unintentional, and I certainly don't mean to villianize him by any means. But cake is a luxury and an artistic tribute to the beauty of food. Buildings are built by men eating meat and potatoes. Solid, robust and maintainable software is developed by programmers who use best practices.

Thursday, August 20, 2009

Vim Tip: Session Management

Instead of having another gigantic essay "Vim Tips, part 2," I decided to just address interesting tid-bits of my .vimrc file one at a time. First up: sessions!

Vim sessions are rudimentary but can easily be enhanced to make working on a project or multiple projects more convenient. I'm a big fan of tabs, so I'll often have several tabs open at a time. When I return to work the next day, I'd like to pick up where I left off as seamlessly as possible. Also, if I get pulled away on something else temporarily, I'd like to be able to save my current state, jump to the task at hand, and save its session, as well, in case I have to go back to it again after it's complete.

To this end, I've put the following in my .vimrc file:

nmap <F3> <ESC>:call LoadSession()<CR>
" don't store any options in sessions
if version >= 700
set sessionoptions=blank,buffers,curdir,tabpages,winpos,folds
endif

" automatically update session, if loaded
let s:sessionloaded = 0
function LoadSession()
source Session.vim
let s:sessionloaded = 1
endfunction
function SaveSession()
if s:sessionloaded == 1
mksession!
end
endfunction
autocmd VimLeave * call SaveSession()

To start a session in vim, use the following:

:mksession

This will create a Session.vim file in your CWD. Now, next time you open vim, you can simply hit F3 and your session will be restored. Of course, this little snippet does more than just ":source Session.vim".

When you hit F3, my script sets a flag that means we're currently using a saved session. When you quit vim, it will automatically re-save your session, so when you restore it next time (by hitting F3 again), vim will be returned to the exact state it was in when you quit. It's a very simple hack, but it saves a lot of time.

One improvement I'd like to make is to be able, somehow, to select which session file to use. Suppose I have Project1.vim and Project2.vim ("Session.vim" is the default name, but isn't required). I'd like to be able to select which project I'm working on, and the appropriate session file would be loaded.