Thursday, April 17, 2008

Be a Better Programmer

Years ago, I became interested in Lisp and embarked on a path from which I would never return. I learned a good chunk of the language and spent a lot of late nights sitting inside the interactive shell provided by the CMUCL Lisp interpreter. While I don't do much Lisp programming these days, I've since incorporated elements of functional programming into all the code I write. Two basic tenants I've adopted as widely as possible are, "rely on return values whenever possible" and "avoid side effects". You can apply those tenants to OOP, procedural programming or really any style. Basically, avoid passing a pointer (or reference) to a function (or method) unless performance dictates otherwise. Rely on a single return value whenever possible. Returning an array is fine if it makes sense in the context of the method (array_unique() for example), but if your method is returning two distinctly different values, it might be a good idea to refactor. Avoiding side effects means that every method you write should do one thing. If your method prints and returns a data structure, consider refactoring into two methods (one that returns, one that prints). Return values should be as consistent as possible across your program.

The Ruby programming language works on the "principle of least surprise". You don't have to be a Ruby programmer to follow this very simple but effective rule. No matter what language you're writing in, try to think of what the "average programmer" would expect your method to accept and return. There's a time and a place for condensing twenty lines of code into two or three insanely terse pieces of syntactic sugar, but simplicity generally rules the day.

Python, Perl, Ruby, and PHP provide a lot of the facilities that made Lisp so popular to begin with. One such facility is an interactive shell. When I'm writing code, I don't waste time guessing exactly how a method or piece of code is going to behave. I have an interactive shell running alongside my editor window most of the time. This allows me to quickly evaluate a methods behavior if I'm not sure exactly what it will return. Here are instructions on firing up interactive shells in those languages.

If you don't currently program interactively, I highly suggest trying it. You'll get your work done faster and there will be less bugs at runtime.

Python:

Being a language with batteries included, all you have to do is invoke the python interpreter, and it drops you directly into an interactive shell.

travis@travis-desktop:/home/travis% python
Python 2.5.1 (r251:54863, Mar 7 2008, 04:10:12)
[GCC 4.1.3 20070929 (prerelease) (Ubuntu 4.1.2-16ubuntu2)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 1 + 1
2

Perl:

Perl's debugger provides an interactive shell. It doesn't have readline support by default, which is a minus, but it still gets the job done effectively.

travis@travis-desktop:/home/travis% perl -d # press ctrl-d after you hit enter (sends EOF)

Loading DB routines from perl5db.pl version 1.28
Editor support available.

Enter h or `h h' for help, or `man perldebug' for more help.

Debugged program terminated. Use q to quit or R to restart,
use o inhibit_exit to avoid stopping after program termination,
h q, h R or h o to get additional info.

DB<1> x 1 + 1
0 1

Ruby:

Ruby uses a program called irb to provide an interactive shell. Depending on your Ruby distribution you may have to install it separately using your package manager of choice. Once it's installed, irb provides readline support and is on par with the python's interactive shell.

travis@travis-desktop:/home/travis% irb
irb(main):001:0> 1 + 1
=> 2

PHP:

I have pretty mixed feelings about PHP, but that's another rant entirely. PHP includes an interactive mode out of the box, but a fatal error will drop you back to the command-line. Either way, it's better than nothing.

travis@travis-desktop:/home/travis/Apps% php -a
Interactive mode enabled

<?
echo 1 + 1, "\n";
2
?>

A better solution is to use this third-party app:

http://jan.kneschke.de/projects/php-shell/

Once it's installed, you have a full-featured interactive shell with readline support. It also handles exceptions gracefully.

travis@travis-desktop:/home/travis% php-shell.sh
PHP-Shell - Version 0.3.1, with readline() support
(c) 2006, Jan Kneschke <jan@kneschke.de>

>> use '?' to open the inline help

>> 1 + 1
2

5 comments:

Anonymous said...

Interesting article, thank you very much !

Also I have one question :
Did you considere raising/throwing exceptions instead of returning error codes ?

Exceptions allow to seperate the error flow from the normal flow, which make it easier and safier to identify/handle errors, instead of having them spread out all around your code.

Also by using exceptions you do not have to test for return code at each function level : you only catch exceptions and handle errors in your key entry points.

Exceptions make you write less code (and less bugs as a consequence), and safer code by clearly seperating the error flow. It also make your code more readable.

Also I have to admit that not every language support exceptions ...

Perl as a "home made" exception handling. Python, Ruby, Java, C++, C# ... have a more conventional one. I do not know about lisp ...

Travis Whitton said...

Thanks for the comment. Actually, I do use exceptions quite regularly; although, I didn't mention them in the post. Generally speaking, if I can handle an error in the context of a method, I will. If the failure will raise issue with the normal runtime operation of the program, I use try/catch, begin/rescue/end, etc...

How heavily they're used, is generally a matter of programming style, language, and preference in my experience. I worked on some Java code recently and employed exception handling much more rigorously than I usually do. When in Rome...

Like most idioms, they make sense in some situations and don't in others.

function sum(a, b) {
return (int)a + (int)b
}

The above isn't real code but in the "imaginary" language, assuming casting invalid data to an int would result in a zero, there's no possible input that would raise an exception.

Something like databaseConnect() has a lot more potential for error. In that kind of context, I'd almost always utilize exceptions to simplify flow.

So like most things, it depends, but thanks for raising a good point and visiting the blog!

Anonymous said...

You can get the equivalent of an interactive shell for browser-based Javascript with Firebug's console. Firebug has saved me uncountable hours debugging Javascript.

Anonymous said...

If you're using the interactive Python shell a lot you should give ipython (http://ipython.scipy.org/moin/) a try. It is a nice extension of the default Python shell with all the default functionality, as well as many new nifty features.

Travis Whitton said...

Next time I'm doing some Python coding, I'll check it out. Thanks for the tip!