« April 2008 | Main | June 2008 »

May 24, 2008

Imperative to a Fault

One of the most alarming things to me in the industry today is the almost superstitious aversion to the use of expressions. For example, consider the following snippet:

protected virtual bool SameNumberOfParametersAndValues
(DbCommand command, object[] values)
{
int numberOfParametersToStoredProcedure = command.Parameters.Count;
int numberOfValuesProvidedForStoredProcedure = values.Length;
return
numberOfParametersToStoredProcedure == numberOfValuesProvidedForStoredProcedure;
}

When I first came across this I thought it was a joke. But then I realized this was a recommended best practice! This gem of a function is a method of the Database class in the latest version of the Enterprise Library from the Patterns and Practices group at Microsoft. This is the kind of code they are saying we should all be writing!

Never mind the run-time inefficiencies of using this method (we hope the c# compiler would be smart enough to optimize it away, but it's likely not the sort of thing for which a compiler-writer would expect to have to optimize), it's almost impenetrable conceptually. In the name of making the code 'easier to read' they have completely hidden what is actually happening. Are we seriously claiming that the simple expression command.Parameters.Count == values.Length is unreadable? This really warranted building a separate function, then declaring local variables to store the values to be compared, then comparing the values? And yes, I realize the function is virtual, the point still stands within the function itself.

The thing is, I wish this were an isolated instance and that code snippets like this were few and far between, but this is not the case. I come across code like this on a daily basis, code that avoids the use of composition of expressions as though it was some toxic ingredient. As though in order to write good code, each line must contain only a single operation (which BTW the above code does not reach this ideal, it accesses a property of a property, as well as initializes a variable on the same line on which it is declared).

Now the first argument in support of this extreme position is that it makes it easier to debug. Well, that's simply not the case with a modern debugger, it's just as easy to watch the value of a property as part of an expression as it is to watch a local variable. No dice. Not to mention the fact that this is the implementation tail wagging the programmer dog. Shouldn't our first priority be to write code that is easy to read and understand?

I suspect that the 'it's easier to debug' argument is really just an attempt to defend this style of programming. I believe that what is really at fault here is languages that have beaten the expressive tendency out of developers. Languages like Visual Basic that until very recently completely lacked the ability to pass the results of an expression as an argument to another operation. In a language like that, it's easy to see how an 'Imperative to a Fault' style would evolve and even become a 'best practice' leading to abysmal functions like the one above, to the point that even now, when the language does provide closure, the 'best practice' prevents its usage!

Far better to embrace the expressive potential of languages and improve readability and maintainability of code by moving towards the more declarative ideal.

Bryn Rhodes
Alphora

 

May 23, 2008

A problem with reverse type inference in C#

Anonymous methods and lambda expressions, language features of C# introduced in versions 2.0 and 3.0, have the interesting quality that they infer their type from their context.  In other words, rather than determining the type of the expression from operator(s) of the expression, this particular kind of selector is given its type based on the assignment/argument it fulfills.

delegate void D(x : integer);
D d = delegate { ... }; // the type of the anonymous method becomes D

This is of course the reverse of the usual type inference direction.  To illustrate the strangeness of this concept, imagine that this kind of thing worked with other types:

int[] x = new [] { 1, 2, 3 };  // right array selector "becomes" of type int[]

This seems harmless enough.  I've been observing this odd reverse type inference with interest, wondering whether it is a good thing or not.  I must say there were a few times when we considered similar such things in D4.

Today, however, this "feature" bit me and I think I see now why it is probably never a good idea.  Here is a simplified illustration of the situation:

delegate int D();
D d = delegate { return 1.0; };

What do you suppose the error will be for this obvious type mix up?  Answer: "Cannot convert from 'anonymous method' to 'D'".  Doing the following, however, results in the expected "Cannot implicitly convert type 'decimal' to 'int'" error:

D d = (D)delegate { return 1.0; };

In a more complicated situation, one is left scratching one's head wondering if everything learned about delegates was a lie.  It seems that trying to reverse the direction if type inference could lead to all sorts of similar chicken-or-egg conundrums.  It seems that a more elegant solution would have been to provide some form of implicit conversion.

My take-aways are thus:

1. If I ever see a similar delegate conversion error, my first action will be to cast the anonymous delegate to what I expect it to be so that I may see the "real" error.

2. Treat all temptations to do reverse type inference with much suspicion.

May 13, 2008

Where Do I Get Help For This Error?

This ranks among the best browsing experiences I've ever had:

The best message ever!