Function as Constructor and other First-Class Class Questions

| 4 Comments

When you work with other people on a project, you either end up adopting some of their coding styles or you end up not working with other people on that project. When I've worked with teams of developers instead of on my own, I've noticed that a lot of Perl code tends toward ProjectName::Parent::Class::Hierarchy::Package::Name unless very carefully pruned with something like roles.

I found myself in a situation like that recently, and I noticed it because I found myself typing class names like ProjectName::Order::Family::Genus::Species::Subspecies repeatedly. The obsessive automator in my brain said "You should find a way to shorten that". Occasionally I've written code like this:

package MyClass {

    sub entity_class    { 'ProjectName::Entity' }

    sub container_class { 'ProjectName::Container' }

    sub do_something
    {
        my $self      = shift;
        my $entity    = $self->entity_class->new( ... );
        my $container = $self->container_class->new( ... );

        $container->add_entity( $entity );

        ...
    }
}

This has a couple of benefits:

  • It isolates the names of the dependent classes into a single, overridable (even injectable) place
  • It's shorter
  • It's unambiguous to parse, in that Perl will always understand the method call used to call the constructor as a method call

Recently I found myself thinking about Python instead. Even though Perl 5 and Python have essentially the same object system underneath (everything is a big bag of names and values and methods aren't all that special and there's essentially no distinction between a function and a method and did you know that everything you import into a class becomes part of its public interface?), the syntax differs. For example, Python doesn't have explicit constructor methods. To construct an object, call a function with the same name as the name of the class:

obj = Class();

That has the advantage that it's shorter and unambiguous. (It has the disadvantage that it looks like a function call, unless you hew to the convention that functions all start with lowercase names and class names all start with uppercase names.)

It's still shorter.

I found myself idly wondering if there were some way to make this available in Perl. (I know about aliased, but I haven't used it with something like namespace::autoclean, which is the obvious improvement.) How would that look?

I like the brevity and I like the unambiguity in parsing, but writing:

use aliased 'ProjectName::Order::Family::Genus::Species::Subspecies';
use namespace::autoclean;

my $species = Subspecies( ... );

... just feels wrong to me. It's a class method. Invoking a class method should look like invoking a method.

Way back in the day, the Perl 6 discussion turned to first-class classes, and we talked about anointing a new sigil. (My favorite was ¢.) I don't know how well this would work in Perl 5, but free yourself from the constraints of the actual for a moment and consider the possibilities. If we could refer to classes as first-class entities literally in source code:

  • We could give them unambiguous aliases without confusing them for functions or methods
  • Constructing an object via the class object would never be ambiguous to the parser
  • Method lookup and concomitant optimizations would be easier
  • We might be able to find more typeful optimizations
  • Similar things (method invocation) would continue to look similar, while different things (object method invocation versus class method invocation) would look different

Of course, you could just as well argue that Perl 5 doesn't need another sigil.

I haven't convinced myself that I like this idea, but it does keep coming up. It has the feel of an intriguing idea, but I can't tell whether it's good, bad, or ugly. The right approach may instead be to flatten the hierarchy of classes in the program so that names are shorter overall... but even so, the idea of first-class classes has its benefits.

4 Comments

If you are looking at a new sigil, please look at non-US keyboards!

While the dollar symbol is present on a lot of national keyboards, the cent symbol is not. I'm working on a British-English keyboard and while $ is shift-4, the only way i've got for getting ยข is Alt+0162 on the numpad, very much less then convenient.

Actually with aliased, the syntax is:

use aliased 'ProjectName::Order::Family::Genus::Species::Subspecies';
my $species = Subspecies->new( ... );

The Subspecies(...) syntax can be achieved using Acme::Constructor::Pythonic.

use Acme::Constructor::Pythonic qw(
   ProjectName::Order::Family::Genus::Species::Subspecies
);
my $species = Subspecies( ... );

Occasionallu I'll do:

{
package Foo::Class;
sub new { ... }
*Foo::Class = \&Foo::Class::new;
1;
}

Which then allows:

Foo::Class->(...);

as a constructor.

I haven't decided yet whether I like this enough to incorporate into any large projects; it feels pretty idiolectic.

No sigil needed! I was discussing this very topic with a co-worker a few weeks ago, then mentioned it in a blog comment, and tobyink++ posted this the next morning: https://metacpan.org/module/Acme::Constructor::Pythonic

Modern Perl: The Book

cover image for Modern Perl: the book

The best Perl Programmers read Modern Perl: The Book.

sponsored by the How to Make a Smoothie guide

Categories

Pages

About this Entry

This page contains a single entry by chromatic published on November 7, 2012 8:57 PM.

The Hasty Generalization of Language Features was the previous entry in this blog.

API as Documentation is the next entry in this blog.

Find recent content on the main index or look in the archives to find all content.


Powered by the Perl programming language

what is programming?