Toward Coding Without Conditionals

| 2 Comments

My favorite refactoring is Replace Conditional with Polymorphism. If I were to make alist of everything I've learned the hard way in programming, this refactoring would be at the top of the list near "Make it easy to test always" and "Make it difficult to misuse".

I'm sympathetic to the view in interface design that the fewer choices, the better. I believe that push for simplicity comes from the desire to force the design to consider the right choices. If you get the default behaviors right, if you choose the right things to make easy and obvious, and if you don't forbid people from extending your code or product to do special things, you have achieved something lovely and worthwhile.

When we design things, we have to find a balance of flexibility (people should be able to use this for purposes we can't imagine right now) and simplicity (we should emphasize the few things we do well and right). That goes for writing code, from designing APIs to each line of code we right.

APIs are user interfaces for programmers, sure. The individual lines of code we write—within our functions and our methods and our loop bodies—are APIs for understanding the problems we're trying to solve.

When I write code, I want to write boring code. I want to write code so boring that it's easy to overlook the code because the intent of the code is so staggeringly obvious. Boring code is straightforward. Boring code doesn't have a lot of corner cases to get hung up on. Boring code you can glance over and see what it's doing.

Boring code gets out of your way.

A lot of the code I've written recently looks like this:

sub method
{
    my ($self, $collection) = @_;

    while (my $item = $collection->next)
    {
        next if $self->do_this( $item );
        $self->do_that( $item );
    }
}

Sometimes there are preconditions:

sub method
{
    my ($self, $collection) = @_;

    return unless $collection->has_some_attribute;

    while (my $item = $collection->next)
    {
        next if $self->do_this( $item );
        $self->do_that( $item );
    }
}

Sometimes there are postconditions:

sub method
{
    my ($self, $collection) = @_;

    my @failures;

    while (my $item = $collection->next)
    {
        next if $self->do_this( $item );
        $self->do_that( $item );
        push @failures, $item;
    }

    return unless @failures;
    return \@failures;
}

I like this form less than the previous two; there's too much structural code for my taste. It's still pretty boring though.

One of my favorite features of generic or polymorphic programming is that you can push the "What should I do if this condition does not apply?" question into methods or pattern matches (of the Common Lisp or ML or Haskell sense, not regular expressions). Dealing with an empty list in Haskell is easy:

sumList []     = 0
sumList (x:xs) = x + sumList xs

... and the corresponding Perl is:

sub sum_list
{
    return 0 unless @_;

    my ($first, @last) = @_;
    return $first + sum_list( @last );
}

Math's a bad example for OO polymorphism, but I would rather write:

for my $item (@collection)
{
    $item->save;
}

... than:

for my $item (@collection)
{
    $item->save if $item->>is_dirty;
}

... and let save() decide what to do. When I phrase it like that, it's obvious. It's the object's responsibility to decide what to do. Maybe that's why non-guard-clause conditionals bother me more and more: they're a sign that I need to move around responsibilities more to cluster them where they really belong. As silly as it seems to say that a little word like if or unless is a sign something's wrong, those little words can be a sign that the current code under consideration is doing too much. It needs to be simpler.

(There's a friction, of course, to breaking classes into smaller domains of responsibility, but that's the subject of a different post.)

2 Comments

My rule of thumb about when I'm allowed a conditional is that ... if $self->query() is usually fine (until it becomes a message that I need a new class) but that ... if $that->query() is usually wrong and ... if $that->attribute() eq ... is a huge red flag. The Smalltalk pattern that holds that accessors go in the 'private' protocol is one that makes more and more sense to me.

I wish Perl 5 had classes with less inertia. It wouldn't have to go as far as Smalltalk, but if I felt classes were lighter and more agile, I'd use a lot more little classes.

Am I the only one?

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 May 24, 2012 2:16 PM.

The Current Sub in Perl 5.16 was the previous entry in this blog.

Keep Your ResultSets DRY 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?