John Fremlin's blog: Purely effective programming

Posted 2012-06-30 22:00:00 GMT

Every function should either perform a calculation or make a call to a remote machine or another process. If a piece of code does not perform one of these useful activities why does it exist? Delete it!

Why should anybody write pointless code like this at all? One reason is that when a weak programmer encounters a problem he or she cannot see how to solve, he or she will create an abstract layer (unimplemented) that gives the solution, and then will try to fill in the layer. This is obviously bad but somehow all too easy to nod along to. Another cause is a desire code ownership: someone wants to own or rewrite a slightly broken subsystem, but can't figure out how to modify it in place, or wish to establish ownership of a new replacement system, so they call down to the old system after mangling the parameters slightly.

What about the C++ pimpl pattern where the top class forwards every call to its implementation class, and similar implementation hiding techniques in Java? Well, like all object orientated abstractions this technique can be useful for large projects with complex and varying consumers. For projects that have merely become large due to the layer after layer of fragile and badly conceived implementation hiding mechanisms, or for parts of those projects which cannot conceivably have more than one caller, there really is no point — and the implementation hiding makes life harder for anybody trying to debug or modify the code.

My favourite example is the pattern where a shim class exposes a bunch of different methods, translates them into an Enum method code with a parameters bundle, and passes them to another class which translates back from the enum to a method call on an implementation class that returns something completely innocuous. This means that adding a parameter requires modifying probably four or five files (given that though there will be only one implementation of each of these components, they will be exposed as descending from abstract interfaces). Unless really carefully thought through the parameters passed to an interface expose a lot of information about the implementation, so little implementation specificity has been hidden at all.

If you have to pass though calls to another interface, make the calls as terse and generic as possible. Don't inspect the parameters. Purely effective programming — if your code doesn't do something, delete it!

I like the rigour of this approach. It is at least the right default stance.

And it gibes nicely with Rich Hickey's recent campaign for "keeping data simple" -- using basic data types wherever possible, instead of the OOD habit of defining classes that just wrap data in a domain-ish mini-language.

However, doesn't this ignore that sometimes it really is worth the extra code just to present a semantically simpler, handier interface to a complex module? We nod along to the guy implementing to fill his own interface, because often he's clarified the problem by expressing it in that interface.

And a lot of code would get harder to read if you stripped out the innumerable small utility functions that just wrap more general bits of API. Just because the compiler can probably strip them out, doesn't mean we should as well...

Another way to put it: If you write purely effective functions, you will write fewer lines of code, which is good. But do you now need some *other* practice to contain the complexity per screenful of code?

Posted 2012-07-02 22:32:23 GMT by Anonymous from 149.241.67.174

Post a comment