Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any reason to write terse code in Java/C#/C++?

Did you ever find yourself writing terse code in Java, C# or C++?

If so, why? Do you think there are any situations in which this should be acceptable, given the situations in which these languages are used?

like image 685
Yuval Adam Avatar asked Mar 20 '09 14:03

Yuval Adam


People also ask

What is terse code?

Terse code is that which is reduced to its minimum required syntax to achieve its means, while advocates of verbose code favour redundancy and legibility to aid understanding.

Why is Java so verbose?

Java verbosity has another side: it provides explicit context and programmers' intent. Which reduces mental overhead and simplifies reading the code. Since most of the time we're reading code, this verbosity makes Java code simpler to support and maintain, especially in long run.

Is c# verbose?

C# is very verbose and tedious compared to more expressive languages - having to deal with CLR types/API at runtime while using a language with very limited expressiveness (C#) is not very productive.


3 Answers

It depends on your definition of 'terse'.

If you mean 'short and to the point', it closely matches my vision for good code.

If you mean 'cryptic', then there's a problem.

like image 184
Joel Coehoorn Avatar answered Nov 06 '22 17:11

Joel Coehoorn


Code should be as terse as necessary and no more. :)

Flippant remarks aside there are several factors affecting just how terse (or otherwise) it should be:

  • Lifespan.

    • Often longer than you think :)
  • Probability of bugs.

    • How likely is dependent on many things but the original coder has a big part to play in this.
  • Computers reading (parsing) it.

    • Not just the compiler, think intellisense and the like.
  • Humans reading it

    • sometimes raw, sometimes with the help of programs like diff tools.
    • sometimes not the same person that wrote it.
  • Required performance characteristics.

All these things combine to produce a set of sometimes competing forces which may want more or less verbosity.

Balancing these is the key to effective development. Which are more important is totally dependent on the problem your software is trying to solve.

First off lets take the easy bits:

Computers.

When they read your code they are quite capable of doing so irrespective of the verbosity. They might be a little slower but this is something that is normally hard to measure (it is unlikely you will go beyond 1 or two orders of magnitude of verbosity than the minimum theoretical possibility). Notable exceptions are where you are (ab)using something like meta programming via a preprocessor to do lots of expansion for you. This can take a long time when compiling. Here you must decide if this trade off is worth it.

Humans.

Generally they will be people with similar context to you and they will be reading the source in a similar situation to when you wrote it. By this it means that if the function was in a file/class/module called Foo then there is no need to go putting Foo in front of things, the Foo aspect of it should be quite clear from context. This makes changing this aspect easier in future.

Programmers familiar with the idioms of the language/style of programming you are using will be quite capable of understanding several constructs which are extremely terse. Loop index variables called 'i' for example are as terse as you can get but are normally not a problem until your loop becomes large.
Here you see an interesting dichotomy. The value of terseness is often proportional to the complexity of the block of code within which it resides. As this block becomes more terse the variables within it benefit more from being shrunk. By writing code in functions/classes with limited responsibility it becomes easier and more helpful to keep things terse as there is less scope for confusion on the part of a human. Paradoxically this can lead to the need for the context to be more explicit, thus longer method and class names.

Lifespan

The lifespan and probability of bugs factor into how often you will have to either read the code or debug through it. Many debuggers support break points at multiple points on a line (correctly spotting where there are two statements) but some do not. Therefore care should be taken on if you intend to break point within it a lot to make sure you can place and control these with minimal effort.

If the code has a low probability of bugs but a long lifespan you have another interesting situation. The probability of the code being comprehensible when you come to need to change it is much lower (you will have a worse memory or may not even be there any more). This code therefore will benefit from being slightly less terse than normal.

Performance

On occasion you might have to sacrifice a compact but clear representation of something to satisfy a performance goal, perhaps you must bit pack for example, never a nice thing to read in code but unavoidable if you have to fit in a certain amount of memory. Occasions like these are hopefully rare.

General Concepts

Some language constructs can encourage terse code (automatic properties, anonymous inner classes, lambdas to name but a few). Where these concepts make sense to use use them judiciously. The idea is that they reduce boiler plate and expose intent.

If you do the same thing repeatedly and have a certain amount of code duplication consider a shared function/class/module but remember that if you must make the shared code less clear (say an additional if statement or unused variables in one of the code paths) then you may not have a net win.

Type inference is powerful but remember that the compiler is sometimes much better at it than a human. If you are saying flibble x = new flibble() then var x = new flibble() is no stretch at all (and gets better as flibble gets bigger). Compare with var flibble = SomeMethodWhoseReturnTypeIsNotClear(). Common sense helps here, if you would never have to use intellisense to work it out you certainly should consider using it.

Some other useful (and terse) rules of thumb:

  • Multiple actions on a single line often confuse humans.
  • Side effects often confuse humans (++x or x++ don't matter at all conceptually unless part of a wider expression for example)
  • Indentation helps most humans to infer structure far more than brackets do
  • Precedence rules are often 'internalized' by humans rather than remembered as a set of rules. This means that needless (to the compiler) bracketing can be harmful for readability where the idiom is common but useful where the usage is not so common.
  • Logical not is often a single character. When using this in an if statement consider whether it can be restructured to not require it. This may not be possible (if the resulting contortions to variable/method name or ordering of code outweigh the removal leave it in)
like image 38
ShuggyCoUk Avatar answered Nov 06 '22 15:11

ShuggyCoUk


It depends on what exactly you mean by "terse". I certainly like to write concise code which expresses exactly what I want to achieve in the simplest possible manner. For example, I love the way LINQ lets me express a data pipeline rather than the "old" way of writing loops to transform or filter collections, or find the largest value etc. That's just duplicate code which should be in a template method somewhere.

On the other hand, shorter code isn't always more readable than longer code. The conditional operator is the subject of controversy on this front. Is:

Foo x = null;
if (condition)
{
    x = y;
}
else
{
    x = z;
}

more or less readable than:

Foo x = condition ? y : z;

Well, if "condition", "y" and "z" are all fairly simple, the conditional operator wins. If you have to go through hoops to make "y" and "z" single expressions where executing multiple statements would be more readable, then the if/else form is likely to be more readable.

In short, I write the most readable code I can. That's often, but not always, terse code.

like image 33
Jon Skeet Avatar answered Nov 06 '22 15:11

Jon Skeet