Question

What language, in your opinion, allows the average programmer to output features with the least amount of hard-to-find bugs? This is of course, a very broad question, and I'm interested in very broad and general answers and wisdoms.

Personally I find that I spend very little time looking for strange bugs in Java and C# programs, while C++ code has its distinct set of recurring bugs, and Python/similar has its own set of common and silly bugs that would be detected by the compiler in other languages.

Also I find it hard to consider functional languages in this regard, because I've never seen a big and complex program written in entirely functional code. Your input please.

Edit: Completely arbitrary clarification of hard-to-find bug: Takes more than 15 minutes to reproduce, or more than 1 hour to find cause of and fix.

Forgive me if this is a duplicate, but I didn't find anything on this specific topic.

Was it helpful?

Solution

The more powerful the type system of the language, the more bugs will be caught at the compile time itself.

The following figure compares some of the well known programming languages in terms of the power, simplicity, and safety of their type systems. [ Source ]

alt text

*Factoring in the ability to use unsafe constructs.

C# gets stuffed into the unsafe row because of the "unsafe" keyword and associated pointer machinery. But if you want to think of these as a kind of inline foreign function mechanism feel free to bump C# skyward.

I've marked Haskell '98 as pure but GHC Haskell as not pure due to the unsafe* family of functions. If you disable unsafe* then jump GHC Haskell up accordingly.

OTHER TIPS

In my opinion Haskell helps you avoid some common sources of errors:

  • it's purely functional: functions cannot have (unintentional) side-effects and this makes multicore programming easier and less error-prone
  • it is strongly typed: you can not e.g. accidentally mix bool, char, int and float values
  • it's statically typed: many programming errors are caught at compile time
  • null is not part of value type definitions: by this you avoid the billion dollar mistake
  • there are a lot of ready-made higher-order functions which you can reuse instead of writing your own, possibly faulty, implementations
  • it has a garbage collector: memory errors are almost eliminated (except for "space leaks" due to its lazy evaluation strategy)

Traditionally the hardest to find bugs are race-conditions in multi-threaded applications as they are

  • almost impossible to reproduce
  • can be very subtle

Hence you need languages that manage the parallism for you as much and unintrusively as possible. These are not yet mainstream. Java does some, but leave you with the hard part.

To my understanding, you need a functional language since the "no sideeffects" is the thing that in the first place makes the two bullet points go away. I've seen that work is ongoing in transparently making Haskell an efficient multi-thread language, and I believe Fortress is designed from the ground up to be an efficient parallel language.


Edit: In Java Executors handle even more of the hard parts. You need to make the individual tasks conform to the Callable interface.

Ada is designed so that as much as possible is caught at compile-time rather than run-time. What this means is that it often takes about 10x longer to get a program in Ada to compile than the equivalent would in Java say, but when it does compile you can be much more confident that whole classes of bugs will not manifest themselves when the program's run.

First a definition: a hard-to-find bug, as I understand it, is a bug that can be reproduced but the cause is hard to find.

Probably the most important aspect is what I would call narrowness, i.e. how far can a bug escape, how large is the scope a bug can potentially influence. In languages like C, a bug, e.g. a negative array index or uninitialized pointer, can affect literally everything everywhere in the whole program, so in the worst case, you have to check everything everywhere to find the source of your problem.

Good languages in that regard support access modifiers and enforce them in a way that makes it hard or impossible to bypass them. Good languages encourage you to limit the scope of your variables, instead of making it too easy to have global variables (e.g. "everything not explicitely declared is a global variable with a default type and value").

The second important aspect is concurrency. Race conditions are generally hard to reproduce and therefore hard to find. Good languages offer easy-to-use synchronisation mechanisms, and their standard libs are thread safe where necessary.

This already completes my list; other things like strong typing help to catch bugs at compile time, but those bugs probably wouldn't be hard to find later.

Considering all that, I'd say that Java and C#, and many other languages in the JVM and .net world, are suitable to avoid hard-to-find bugs.

Since Excel is the most widely used DSL, I'll go with Excel. (excluding VBA of course)

It fits the bill:

  • It's always easy to reproduce (here's a spreadsheet - it's not working)
  • It's pretty easy to find the bug, as it's totally "functional" - start with the cell that's wrong and trace back all of its dependencies.

This is a difficult question because most bugs aren't the fault of the language itself - rather they are the fault of developers making mistakes in how they use the language.

I believe there are several aspects of language features that affect the likelihood of bugs:

  • Interactivity - dynamic languages with REPLs encourage interaction / experimentation with running programs and much smaller code/test cycles. If you believe that iteration is a good way to discover clean simple solutions and detect/eliminate bugs then this would tend to favour interactive languages.

  • Expressiveness - if code is shorter and has less boilerplate / incidental complexity then it's easier to see bugs / logic errors.

  • Type safety - the more compile time checking, the more bugs will be caught by the compiler so in general type safety is a good thing. However these usually aren't hard to find bugs - even in a fully dynamic language the wrong type in a data structure will usually cause a very obvious runtime error, and TDD almost always picks up these kind of bugs.

  • Immutability - a lot of hard bugs are due to complex interactions of mutable state. Languages that emphasise immutability (Haskell, Clojure, Erlang) have an enormous advantage by eschewing mutability

  • Functional programming - functional approaches to writing code tend to be more "provably correct" than object oriented code with complex sequences of effects / interactions. My experience is that FP helps avoid tricky bugs - I believe there is some academic research somewhere that I can't currently find that backs this up.

  • Concurrency support - concurrency problems are particular hard to detect and debug which is why this is so important. Anything that requires manual locking is ultimately doomed to fail (and this includes pretty much every object oriented approach to concurrency). Best language I know of in this regard is Clojure - it has a unique approach to managing concurrency that combines software transactional memory with immutable data structures to get a novel, reliable and composable concurrency framework. See http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey for more insights

The less powerful a language is, the less options it gives you to shoot your own foot.

High-level languages like Java and C# will produce less bugs than low-level languages like C++.

Having said that I believe Java is more secure than C#. Java is artificially limited so that an average programmer without advanced knowledge can master it and produce stable programs.

What language, in your opinion, allows the average programmer to output features with the least amount of hard-to-find bugs?

In my opinion, Delphi. Being based on Pascal, the language is simple and intuitive enough for the average programmer (or even inexperienced coders) to pick up easily, and its rich tool and library support make most bugs easy to find.

  • Strong typing and a strict compiler that catches many common errors.
  • Intuitive syntax that doesn't encourage common errors. ("The World's Last Bug," if (alert = RED) {LaunchNukes;}, will not compile, for example.)
  • A well-designed object model that eliminates many of the common C++ OOP errors.
  • Bounds checking and range checking built in to the language, drastically reducing the chances of security problems.
  • Probably the fastest compiler known to man, which increases your productivity and makes it harder to lose your train of thought while waiting on a build.
  • The debugger Visual Studio's debugger wants to be like when it grows up.
  • Leak tracking built directly in to the memory manager, making finding and fixing memory leaks trivial.
  • A large, mature standard library providing prebuilt and pre-tested ways to accomplish common tasks without having to build your own, possibly buggy implementations.
  • Ships with useful tools, such as a powerful logging system and a profiler, to make tracking down problems easier.
  • Strong community support for common issues that aren't in the standard library, including a powerful third-party concurrency library.

One thing to take into account is the turn around time.

For the last five years or so, I've mainly developed web applications in java (JSF, Seam, etc.). Recently I got a new job, and we're using Perl (with Catalyst and Moose).

I'm way more productive in Perl, than I was in Java.

Not needing to compile and (hot)deploy, is one reason. I also find that writing use cases is easier, as it can be done in a more iterative way. And the frameworks in Java seem to be unnecessary complex, at least for the projects I've been involved in.

I guess the number of bugs in my Perl code is more or less the same as the number of bugs in my Java code, it might even be higher. But, I find et easier and faster to find and fix these bugs.

Perhaps surveying the number of tools available for static and dynamic code analysis for every programming language could give an idea. The more tools for a language, it is more likely the language is either very popular among users or very popular in generating hard to find bugs. But I am unable to get Google point me to any study made on this subject. It should also be noted that some languages such as C can be used to work around the underlying hardware bugs as well as work around the wear and tear of the hardware as it ages.

Instead of talking about languages what about talking about language-features

  • java forces you to think about exceptions (throws ...) and you must either publisch or handle these exceptions. Does that realy prevent me from forgetting errorsituations or am i using more exceptions that are derived from SystemException that do not need this handling?
  • what about "design by contract" (http://en.wikipedia.org/wiki/Design_by_contract) that forces me to think about pre and postconditions. I have read that is now possible with c#-4.0.
Licensed under: CC-BY-SA with attribution
scroll top