Shorter is Better?

It is widely believed that compactness is a desirable quality of code. Sadly, I often see people claiming that compactness is an absolute metric, in the sense that shorter code is always superior to longer code.

Leaving aside the issue of how length should be measured (character count? line count? token count? executable size?), here are two different Javascript functions that fulfill the same task: translate an integer in the range 0..13 to roman numerals.


function toRoman1(n) {
return n == 0 ? "" :
n % 5 == 4 ? "I" + toRoman1(n + 1) : n >= 5 ?
(String.fromCharCode(
84 + Math.floor(n / 5)*2)) +
toRoman1(n % 5) : "I" + toRoman1(n - 1)
}

function toRoman2(n) {

if(n < 4 && n > 0)
return "I" + toRoman2(n - 1);

if(n == 4 || n == 9)
return "I" + toRoman2(n + 1);

if(n >= 10)
return "X" + toRoman2(n - 10);

if(n >= 5)
return "V" + toRoman2(n - 5);

return "";
}


The former function, toRoman1(), is much shorter than the latter. Moreover, its cyclomatic complexity is lower (since it has fewer branches) suggesting that it is simpler to understand. Still, for most programmers, toRoman1() is more difficult to read, to write, and to debug.

My advice is to go easy with this "shorter is always better" claim. Just like many other things in programming, the right degree of compactness is also a matter of striking a balance. Or, as Allen Holub puts it: "Good and bad are not absolutes".

Update 1: Fixed the no-return-value issue spotted by Yardena.

Update 2: Added the cyclomatic complexity point.

13 comments :: Shorter is Better?

  1. return (n == 0)? "":
              (n < 4)? "I" + toRoman(n - 1):
              (n == 4 || n == 9)? "I" + toRoman(n + 1):
              (n >= 10) ? "X" + toRoman(n - 10):
                             "V" + toRoman(n - 5);

    and you don't have to repeat *if* and *return* 5 times. Also it is clear that you cover all options, unlike your example, which would not compile in Java requiring unconditional return statement in the end.

  2. Yardena,

    I am not saying that the compact form is always more complicated than the longer one. Many compact forms are clearer.

    However, sometimes an attempt to reduce line count can follow a bad path, ending up with a shorter, but less readable, code.

    In your version, you (successfully) made the code clearer by breaking each line immediately after the colon symbol. This imposed a consistent visual structure which promotes readability.

    In doing so you made the code a little longer (line-count wise) since some of these lines could be consolidated, assuming the standard 80 column line width. This gets us back to my point about trade-offs: you want your code to be short but consistent structure, explicitness and other factors are also important.

  3. Hi Itay,

    I think we agree. Writing correct and clear code is the minimum requirement from a programmer - no one should sacrifice that for brevity. Too often people try to be too smart for their own good, which is what you wanted to demonstrate, I think.

    Nonetheless, true art of programming is IMO in keeping the code small and succinct, in addition, but not at the expense of other factors.

  4. It's a matter of taste as well actually. Using the ternary operation (and formatting it properly) leads to a code that's leans towards functional programming and are appreciated to those who are used to such. The longer one however, I would say, is "clearer" to imperative programmers.

  5. I believe in more readable is better, not shorter is better. Sometimes people confuse these two things.

    You can't get much shorter than K, testing prime in the K language:

    {&/x!/:2_!x}

    Peace
    -stephan

    --
    Blog at http://www.codemonkeyism.com/

  6. Readability always suffers when you compacting the code...

  7. jv, Stephan, Gyorgy: I absolutely agree. I think it boils down to the fact that we can measure length, but we cannot measure readability (as jv said, it is a subjective issue).

    This leads to length being used, too often, as a quality indication: "hey, we have numerical values, we can shove them into a spreadsheet and look at some nice graphs!". Too bad.

  8. I also found out, that debugging is often better when splitting over different statements and lines.

    And as I usually spend also a while debugging the longer and clearer code reduces my debugging time.

  9. I think that if you write short and clear it is the best.
    if people are writing trenary for a while they will know how to read it.
    I don't want that a person will be able to read my code, I want a fellow programmer to read it.
    its about conventions.
    and a short convention is generally better I think, because its easier to read!
    not religiously of course.

  10. Martin: I couldn't agree more. I guess there is a justification for a "longer is sometimes better" post :)

    One quick example is: "if(...) x = a;"
    It is so much easier to debug such a statement if its split across two lines - You can visually see whether the condition holds. No need to guess (after the fact) by inspecting x's value.

  11. Martin, your comment motivated me to write a post whose title is indeed Longer is sometimes better.

    Thanks.

  12. The example looks pretty engineered to prove a point, that is, make 13 the maximum so that the computation of X/V works. (And thereby relying on ascii as charset.)

    Conciseness is not compression by using any way to make the code shorter, it's about expressiveness and removing redundancy. Like factoring out the returns.

    And who spends time in a debugger? :-) I usually find prints in the code easier and faster than getting a program to the state I want in the debugger.

    Besides, first thing to boggle the average programmer is the use of recursion instead of loops.

    And the easiest solution for this exercise is:
    function toRoman(n) { return { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII" } [n]; }

  13. Andreas,

    Maybe it's engineered, but not in the usual sense. It is code that was written at a Coding Dojo that I attended. I didn't just invent it. I really saw it getting written and evolving. On the other hand, you're right that it is not something that I took from production code.

    Which makes me wonder. How would the two fragments look like if we let them evolve to handle every n?

    Like your solution though.

Post a Comment