In A Lesson in the Importance of Language Design, Zbigniew Lukasiak raised the question "How do we argue that this API is more elegant than that?"
It's a good question. There's more art to it than science. Yet good art always includes craft, so I believe that it's possible to identify several principles which contribute to elegance. The first is conciseness: saying a lot with a few words.
The Inelegant Way
This code for declaring a class which inherits from a parent and overrides methods in Perl 5 is idiomatic and familiar to many Perl programmers:
package Some::Class;
use Modern::Perl;
use base 'Parent::Class';
# or
@Some::Class::ISA = 'Parent::Class';
# or if you're paranoid
BEGIN { @Some::Class::ISA = 'Parent::Class' };
# or
use vars 'ISA';
@ISA = 'Parent::Class';
# or
our @ISA = 'Parent::Class';
sub foo { ... }
sub bar { ... }
1;
I've written before about how many Perl specific concepts you need to understand to write or comprehend this code. There are several -- a package is a class, the package global @ISA
contains all parent classes in method resolution order, the base
pragma has certain advantages and disadvantages, methods are subs with an invocant, etc. You don't have to know all of these to write modern Perl, but you should be familiar with the underlying mechanisms to some degree. What's atop is just syntax.
(Careful readers might note that parent may supplant base as the preferred inheritance pragma, for various subtle reasons.)
Would you call this code elegant? There's a simplicity to it, but a lack of uniformity. There's an orthogonal minimalism to other parts of Perl 5. Is it elegant?
Consider an alternative.
The Concise Way
use MooseX::Declare;
use Modern::Perl;
class Some::Class extends Parent::Class {
method foo { ... }
method bar { ... }
}
Can anyone deny that this example is more concise than the first example? Reading it is straightforward, even though the underlying mechanism is the same. The differences are merely syntax.
The second example has little or no superfluous concepts. It does not
expose the underlying mechanism. It does not (ab)use other Perl concepts (such
as the import()
mechanism of use
) visibly. It reads
declaratively. Is there anything in the code that's unnecessary?
It's concise.
Not all elegant code needs special syntax, but sometimes a little bit of syntax can make a lot of inelegance just go away.
It would be even more concise if you didn't load no-op modules :)
While I believe that Moose is a powerful way forward for Perl (and offers a fantastic object system), I think MooseX::Declare has the ability to completely turn around people's attitudes towards Perl. It not only brings Perl up to par syntactically with many modern languages, it exceeds them in many areas. Now if only this syntax was core (sigh).
By the way I've just finished reading The Little Manual of API Design. This is the first guide to API writing I've encountered - are there any other?