Monday, 26 November 2007

What makes code beautiful?

Saw this webcast a couple of weeks ago where Marcel Molina explains the notion of beautiful code. And I really recommend anyone writing code to have a look at it (totally irrespective of the fact he uses a ruby example...). If you look at the progress bar of the presentation, it looks like it's really long, but only the first half is the actual presentation, followed by a lengthy discussion. (The last few minutes have a nice wrap-up by Chad Fowler.)

The talk is basically split into two parts: (a) what is beauty, and (b) how does that apply to coding?

What is beauty?

After going into the history of what philosophers like Plate, Rousseau and others found beautiful, Molina focusses on the three rules set out by Thomas Aquinas: proportion, integrity and clarity. Things that are beautiful comply to these rules. The examples that Molina uses:

  • proportion: suppose the fingers on your hand would be twice or half as long. Wouldn't really be beautiful, would it? Think 'golden ratio' here.
  • integrity: a thing has to be fit for its purpose. Take for example a hammer made of crystal. It might look beautiful, but can't do what it's supposed to. The appearance of beauty is not necessarily the same as real beauty.
  • clarity: it should be clear what something is/does.

How does it apply to coding?

It's straightforward to apply these principles to programming code. First, let's take a look at proportion. The code for simple functionality should be short. Not long. To add two numbers should be a single call. Molina gives an example here of how that had to be done in assembly. Really way too much coding going on to just do that. Here at work, the phrase "Jan can do that in 2 lines of ruby" are often heard (fortunately a little bit less, lately (pfew)). I feel that's not because I'm a code hacker, but because Ruby makes it simple to do simple things. Perl has the same characteristic, but at a price (I'll come back to that later).
Secondly, there's integrity. Does your code do what it's supposed to do? That's the most widely-used notion of good code: do the tests run? But code that does what it has to do can be still be ugly if it doesn't comply to the other principles. Spaghetti, anyone?
Thirdly, there's clarity. How much of your code can your fellow programmers understand by looking at it for less than a day? The fact itself that you have to explain your code might sometimes indicate that you're violating this rule.

One quirk of applying these principles to programming code, is that there's a clear trade-off between them. Some hacked together code might be very proportionate to what it's supposed to be, but in doing so it might become obfuscated and therefore violate the clarity principle. I must say that I've often found it difficult to understand my own perl code after a couple of weeks of not looking at it. Most of this can be attributed to the fact that the language itself needs you to write less clear code ($_, the way objects are implemented, ...).

When you see smelly code, chances are it's one of these three that are the problem.

Why does this matter?

To paraphrase Rousseau: Good code is beautiful code in action. Creating beautiful code (by the above principles) is no guarantee for good code, but it's a pretty good start...

Note: There's a book on Beautiful Code as well, and apparently there's a chapter by Lincoln Stein on his Bio::Graphics library. Better get my hands on that book for ideas for the ruby version.

Monday, 5 November 2007

Named arguments in ruby

One of the main disadvantages of using ruby that I bump into is the absence of named arguments (or keyword parameters). That's no problem for methods taking just two or three arguments, but it does get confusing when you have to be able to pass more than that.

For example, the Bio::Graphics::Panel::Track::Feature#new method takes six arguments: the track it belongs to, a label, a type, the location, an array of subfeatures and a url. (Note: there's only four in the released version. Label and subfeatures are worked on at the moment...) When you get to this number of arguments, deciding what the order should be is almost more difficult than writing the actual code... You really have to start thinking about how the class will be used most often. If the user only has a track and a url, it's a bit unsightly to make him/her create a new feature by typing

my_feature = Feature.new(my_track, nil, nil, nil, [], 'http://www.google.com')

Compare that to

my_feature = Feature.new(:track => my_track, :url => 'http://www.google.com')

The way to get this to work, is to pass a hash in the method definition. There's a few gotchas, however. For instance, you can't define the defaults in the argument list directly.

The following code snippet almost works, but not quite...

class MyClass
def initialize(options = {:name => 'unknown', :size => 0})
@name = options[:name]
@size = options[:size]
end
attr_accessor :name, :size
end

But you can set the defaults like this:

class MyClass
def initialize(options = {})
@name = options[:name] || 'unknown'
@size = options[:size] || 0
end
attr_accessor :name, :size
end

In addition, you have to check if all passed keys make sense. What if a user would use the MyClass#initialize method above with a :age key? It's easy enough to catch that in your #initialize definition, but you have to remember to do that...

Another major disadvantage is the fact itself that you need a workaround in the first place. This means that it makes it a no-no if you want to integrate your classes in a bigger framework. There's no way I should use this approach for Bio::Graphics if it would be integrated with bioruby later. That would result in inconsistency in calling different classes within bioruby, which is the last thing you want...

Let's just hope ruby 2.0 will allow for using keyword parameters. (Pretty please...)