Wednesday 18 June 2008

Early Out

A quick post on multiple return points. Some people will argue that they are a Bad Thing(TM), but I would like to post my reasons for using them in my code. First of all, what do I mean by multiple return points? I mean that a function has more than one return statement in it.

function quiteMessy(param1, param2, param3)
{
if (precondition)
return 0;

for (i = 1 to 10)
{
if (something(i))
return i * param1;
}

doSomeStuff();
doSomeMoreStuff();

if (condition(param2))
return param2 * param3;

doEvenMoreStuff();
andMoreStuff();

val = doSomethingWith(param3);
return val;
}


In this code, there are no less than 4 return points. The trouble happens when you have temporal coupling (requiring that things happen in a certain order).

If this code was written with the requirement that doSomeStuff() was called before exiting, and then the precondition / return 0 code was written afterwards, then the requirement is not met. So yes, in this case, it was wrong to have the multiple exit points.

But I would argue that there are good reasons to write code with multiple exit points.

Yesterday's post recommended that a function should only do one thing. If when calling the quiteMessy() function we expect to calculate a value and call doSomeStuff then that's two things, not one. If we put the two things into separate functions, we would remove the temporal coupling within the quiteMessy() function.

Ok, so reducing the amount we do in one function makes it easier for us to return early on in the function, but I still haven't given you my reason for multiple exit points in a function.

My reason is to improve readability of the function. I tend to base my functions on two possible patterns.

The first is that the entire function's purpose happens at the end of the function. I see the rest of the function as a crescendo, leading up to the one thing that the function does.

If I was going to return early from a function, I'd try to do it as early as possible. So I end up with some kind of precondition at the top of the function (check if it has already been done, check if it's valid to do this now, etc), followed by any setup code which is necessary for the build up to the final action of whatever it is that I want the function to do.

function calculateSomething(param1)
{
// preconditions

if (nonExistent)
return null;

if (invalidParam(param1)
return null;

// setup result

foo = preprocess(param1);
bar = barify(foo);
baz = bar.getBaz();

// return result
result = baz.getValue();
return result;
}


In the code sample above, even though there are three return points in the function, I've kept within the guidelines of the pattern. The first two return points are within the first two statements of the function - the preconditions, there are no return points during the setup of the result, and the main return point is on the last line of the function.

If that sample was written without multiple return points, I would have to nest the calculation within both preconditions.

function calculateSomething(param1)
{
result = null;

if (exists)
{
if (validParam(param1)
{
foo = preprocess(param1);
bar = barify(foo);
baz = bar.getBaz();

result = baz.getValue();
}
}

return result;
}


It no longer feels as though calculating the result is the important thing. I prefer to keep my focus at the top level of the function, rather than nesting several layers in. Nested code is a deviation from the straightforward path of the function, and makes the code more difficult to understand.

The second pattern I base my functions on is doing the thing that I called the function for at the first opportune moment.

One possible example of this would be finding a value.

function findThing(param1)
{
for (ix = 1 to itemCount)
{
candidate = item[ix];

if (candidate.name == param1)
return candidate;
}

return Not_found;
}



Searching through the items in this code sample, I would like to return the result as soon as I find it, but I also have a return point at the end of the function in case the item wasn't found. Without multiple return points, I would have to store the result in a temporary value before returning it - what a waste of time that would be!

Another example of this pattern would be if I had a cached value, or both quick and slow methods of getting the result. (Presumably the quick method has drawbacks, otherwise you'd never use the slow method!).

function calculateOptimal(param1)
{
cachedResult = cached(param1);
if (cachedResult != null)
return cachedResult;

if (canCalcQuickly(param1))
return calcQuickly(param1);

// otherwise

return calcSlowly(param1);
}


I dread to think what the single return point version of this code would look like!

Earlying out of a function is fine, as long as it makes the code easier to read and understand.

One task, One place

Today's advice is nicely summed up by the saying "A place for everything, and everything in it's place." Like any of my advice, you can choose to ignore it, or even contradict it. If anyone has advice I like better, I'll follow their advice instead.

The DRY principal (don't repeat yourself) says that any complex task should only be carried out in one place.

Why? The end user doesn't care if you repeat yourself. The computer certainly doesn't care if you repeat yourself. In the end, it turns out that the people who care whether you repeat yourself are the developers of your code (including yourself). Again, it boils down to programming for people.

Duplication is a Bad Thing(TM) for several reasons. The most obvious reason is that if you have some duplicate code, and discover a bug in that code, you could fix it in one of the places and completely forget about the other places. So in fact, the bug isn't fixed. Now if the duplicate code had been written as one function that was called from the places where that code was needed, instead of duplicated, the bug fix would only need to go in one place.

(I'm going to ignore the advice and repeat myself here because this is important!) The bug fix would only need to go in one place! When you've come up with a fix, there's no need to trawl through the codebase looking for all the places that the fix needs applying because the places that need this code all pass through the single place that you have applied the fix.

The second reason to avoid duplication is that duplication makes code more difficult to read, and comprehend. Which is easier to understand - 10 lines of code, or 20 lines of code? For the sake of argument, let's assume that each of the individual lines is as understandable (or obtuse!) as each other line.

Less lines of code means less to understand, means less to misunderstand! Sure, if someone takes the time to pick apart the code line by line, they might eventually understand it. But first glance isn't going to give us as quick an overview if there is twice as much code there than is really necessary. Also, if you wrap the duplicate code up into a single function, even the function name can help comprehension. Compare

Sample1:

function bigFunc()
{
setDay(15);
bar(fred.location.x, fred.holiday.x);
baz(bob.foobar(X, Y, Z));
abc_def(fred, bob, saz.ABC);
setDay(25);
bar(sam.location.x, sam.holiday.x);
baz(jane.foobar(X, Y, Z));
abc_def(sam, jane, saz.ABC);
}


vs Sample2:

function bigFunc()
{
setDay(15);
arrangeMeeting(fred, bob);

setDay(25);
arrangeMeeting(sam, jane);
}

function arrangeMeeting(Person p1, Person p2)
{
bar(p1.location.x, p1.holiday.x);
baz(p2.foobar(X, Y, Z));
abc_def(p1, p2, saz.ABC);
}


Now the arrangeMeeting() function may still not make much sense, but at least we now know that this chunk of code is meant to arrange a meeting between people. If we are interested in the details of what the bigFunc() does, we only have to read through the horrible mess of bar(), baz() and abc_def() once (if at all). And of course, any mistakes in the meeting code will only need to be corrected once in the arrangeMeeting function.

The two samples lead quite nicely to my second point today, which is:

In any one place, only one task should be carried out.

Instead of having to digest the entirety of the bigFunc() in sample1, (and possibly getting indigestion), the code in bigFunc() in sample2 is much easier to digest. The code that deals with arranging meetings has been moved off into its own function, and we are left with much smaller, bite-size chunks of code to deal with.

If you find that you're writing one comment about several lines of code, several times in a function, then each of those chunks of code could well be ripe for turning into a function of its own.

A program should be like a well structured document, with an overall view, chapters (modules), headings (classes), and sub headings (functions), and it should be easy for a reader to drill down to the place that they're interested in. You can go a long way towards achieving this by having a place for everything, and keeping everything in it's place.

Tuesday 10 June 2008

Open Source User Interfaces

Why can't open source user interfaces be, well, more consistent?

Don't get me wrong - I love open source; but inconsistent interfaces really tarnish the experience for me.

Computers, operating systems, software and interfaces are all a means to an end. That end might be to write a letter, check your email, or play a game, but generally speaking, getting on the computer isn't the end itself.

A good user interface lets us get on with what we want to do in a timely manner. If the interface is consistent with other, similar applications, then we can dive straight into a task without having to learn a new interface. Conversely, a poor user interface can be a real impediment between the user and the task that they're trying to perform.

The developers of firefox have the right idea. The software is cross platform, but feels like an app that is developed for the OS. From the point of view of an MS Windows user, the menus look and behave like real MS Windows. The open and save dialogs are the MS Windows common dialogs. In KDE, the app looks like a KDE app. On a mac the app looks like a mac app. Because the developers of firefox have spent time putting on the UI polish.

Once you have the consistency, you get more efficient at performing the mundane steps between starting up the computer and performing the task that you switched on the computer for in the first place.

Common actions include opening a file, saving a file, closing a file, closing an application. There are often several different ways of performing each of the common tasks - including clicking a button, clicking a menu, using a keyboard shortcut (Ctrl+O), or using accelerator keys (Alt, F, O).

Take away the consistency, and in a small but significant way, you are alienating your users. The beginners, who have only learned one of the methods, get confused and discouraged, and unless you are lucky will leave your program for one that seems more familiar. The power-users will get annoyed that they can't use their tried and tested shortcuts in achieving their goals.

With cross-platform software, there are (at least) two possible approaches to UI design. The first is: you could make the UI consistent across all platforms. The second is you could make the UI on each platform consistent with that platform.

The first approach is probably easier from a developer's point of view, but probably not the best from the end user's point of view, because most end users don't work cross platform.

The Gimp is a nice, free (open source) alternative to photoshop; but every time I try to get into it, I feel lost. Ok, so the menus look a bit like my native Windows menus, but using the file dialogs is like stepping into a foreign country. Nothing is familiar. What is generally an automatic process for me (navigating to a folder and providing a filename) suddenly becomes something that I'm forced to learn again from scratch.

The gimp uses GTK to provide their user interface. The GTK library is cross platform so the developers of an application don't have to worry about making the interface work on different platforms. But it would be nice if the GTK library could use native UI elements instead of emulating them.

Monday 9 June 2008

Data Driven

Is your computing experience software driven or data driven?

If someone's approach is software driven, the software will be started up, then the user will press the "open" button, then use the file dialog browser to navigate to their file, and open it up.

Someone whose approach is data driven will navigate to the file, and double click it, and the file will (hopefully) open in the relevant application.

For me, data driven is the more efficient approach to working with a computer. It's a more natural approach. The end user doesn't decide "I want to use Microsoft Word to edit a letter". They decide "I want to edit the letter to the school," and it just so happens that Word is the software that gets used to edit the letter.

In a maximised explorer window, in a details view, I can usually navigate to the file that interests me within a few seconds. (You can fit more files in a window if you use list or small icon view but then your eye has to search in two dimensions, while alphabetical order only really works in one dimension - which is why I find it most efficient to navigate in a details view.)

If I were to try to open the same file using the application's open-file dialog, it would easily take me twice as long to open the file that I'm interested in.

Occasionally a piece of software will add some useful mechanism to speed up your file loading - the automatic loading of your most recent file, a recent file list, a frequent folders list, or a dialog that you can maximise - but the trouble is, by their nature, software-driven data-loading mechanisms are specific to the software and you can't take advantage of them across all of your software and all of your data.

With a data driven approach, you can set up shortcuts to your frequent folders, and use those shortcuts again and again, regardless of the software that will be used to edit your data.

To me, there's no contest. Data driven beats software driven every time.

Friday 6 June 2008

C# Attributes

Just a short post today. Though I'll try to stick to generally applicable programming techniques / articles / entries, I'll occasionally go language or technology specific.

I'm loving working in C# - I only really started C# programming about a year ago and I'm learning more and more about it and really enjoying it.

When I discovered the Property Grid, I thought it was fantastic. The property grid is used to expose properties to the user. The user can view or edit these properties, depending on whether get and set methods are defined. You can do a whole host of things with the property grid, such as putting properties into categories with the Category attribute, specifying default values with the DefaultValue attribute, and you can even specify that a property isn't shown in the grid with the Browsable attribute.

Pretty soon after learning about the property grid, I was using it for everything. Then one day I decided that it would be useful if I could dynamically change what was shown in the property grid at runtime.

I spent a whole day looking for information on how to change attributes at run time. I learned about reflection, I learned about creating attributes, I learned about applying attributes. But I couldn't find out how to change attributes dynamically.

I could have saved a whole day if I'd learned at the start that attributes are compiled into the executable and can't be changed on the fly.

So I'm posting this so that there's a million to one chance that if somebody else is searching for how to change attributes dynamically, they get to read this early on and don't waste the amount of time I did.

Update: 5th July 2008
I've posted some more information on dynamic property grids, in case you were looking for it.