271 comments

[ 3.8 ms ] story [ 262 ms ] thread
A lot of the Computational Thinking movements seem to stress that "Computer Science is more than just computers!" And that's true, we have a lot more to offer! But at the same time, it's so misleading because so much of our really cool stuff is wrapped up in being able to program. I mean, CS is about solving problems at scale, and computers are how we best solve problems at scale. We can teach lots of carefully crafted things without them, but it's always going to ring a little false, and we won't get students more than an inch-deep into why we're truly cool.
Is it possible to prevent timing attacks, control secure wiping of memory etc. in Eiffel?
My recollection of Eiffel (from 10+ years ago) is that it's a more pure OO system where memory allocation isn't usually done raw, but more like Java or C# -- in the context of constructing an object.

If you were writing OpenSSL in it, and you decided to use system calls then you'd be in the same boat. If you OO modeled it with classes, then you might be more safe. Not sure what it does if you model a bunch of bytes as an array, allocate it and don't manually wipe.

What do you mean by timing attacks? If you mean timing the string comparison on a login attempt, then no way -- no language forces you not to compare byte-by-byte, right? I doubt they even discourage it.

Whenever "language doesn't matter" or "use the right tool for the job" is used in an argument it's quite often as a thought-terminating cliche used as a post-hoc justification for personal prejudices. I think almost everyone intuits that there is at least a partial ordering to language quality and safety, we just often disagree about how that ordering is defined.
"Use the right tool for the job" != "language doesn't matter".

There isn't one language to rule them all. If there were, we'd be able to stop discussing them. But sometimes you need explicit memory management, sometimes you need rapid development, sometimes you need high performance, and sometimes you need a strong library ecosystem. "Right tool for the job" means that getting the language correct is important, but that it's not always going to be the same language.

This "use the right tool for the job" reasoning is just as flawed, though.

Switching between programming languages when you need explicit memory management vs. garbage collection or high performance vs. rapid development incurs a huge cost in interoperability. Should an English speaking person switch to French when they want to discuss love, since that's the right tool for the job? Of course not. If one (natural) language has words or idioms that are useful, they get incorporated into the languages that don't have them.

As the field of programming languages matures, we most certainly will want to pick languages that can rule them all. Languages will be adaptable to support different tradeoffs in the various dimensions you mentioned, without making a bunch of arbitrary ad hoc changes to all of the other dimensions.

Whether there will actually be just one is another question. I'd guess "probably not" for the same reasons that there's not a single natural language. Probably we'll have fragmentation, instead. But it won't be because people are choosing "the right tool for the job" and it won't be a good thing.

While there are a few languages that are paradigm changing, in most cases, I would say that switching languages is more like switching between American English and British English, rather than English and French. When in America, it makes sense to use the right tool for the job (American English), and vice-versa, rather than trying to shoehorn what you normally use into a place it isn't well suited.
In many ways it's much worse than the difference between English and French. If you are fluent in both of those languages, you can read a book that's written with some paragraphs English and some French. Software written in two different languages can't be combined nearly so easily.
> Software written in two different languages can't be combined nearly so easily.

As far as a human reader goes, I don't see why not. In fact, Objective-C and Objective-C++ provide practical examples of how dissimilar languages can be intertwined without detriment to the reader. To add to that, when writing pseudocode, people often do mix metaphors from multiple languages.

Granted, having a machine understand that kind of code to be able to do something with it is more of a challenge without a strict set of rules, but that's a completely different matter.

There are idioms in spoken languages that do not translate to other languages. There may be a literal translation, but it doesn't make sense or are incredibly awkward in a different language. Programming languages share this behavior.
Most current ones do, but this will change as new languages gain the ability to express most of what the others can express.
There are a couple limitations on that. For instance, you cannot have a language that's both total and general recursive.
So have a total fragment in your general recursive language, or encode recursion in your total language. Either is preferable to using an FFI to combine markedly different languages.
That's fine, but you're beginning to talk about a lot of languages glued together instead of one "master language", I think.
So "use the right tool for the job" with the understanding that using multiple tools imposes a cost, and then take that cost into account when figuring out whether they are the right tools for the job.
I wasn't arguing that "use the right tool for job" is wrong. It's a tautology, after all. What's implied by most people using it in the context of programming languages (and what I believe michaelochurch intended to imply) is that there will never be a "best" programming language and that the best programming language to use will always depend on the problem you're solving. My point is that this is wrong. As languages mature, there will emerge a handful of winners, and they will be flexible enough to handle all of the variants mentioned with minimally painful interoperability.
He's right, but he's disingenuous in saying that random line duplication can't cause catastrophic problems in Eiffel. This very specific bug can't happen in Eiffel, but the class of bug can (bug caused by bad merge or accidental, unnoticed line duplication).

If most code were idempotent, functional, immutable, etc -- then we'd start to get there, but usually randomly duplicating lines is going to be an issue unless it's always a syntax error.

I'd say clojure has more of a chance. (1) lots of immutable data and functional style (2) duplicating code lines is likely to result in unbalanced parens -- the unit of atomicity is the form, not a line. Many forms span lines in real code, and many lines contain partial forms (because of nesting).

Still there is plenty of clojure code that is line oriented (e.g. data declarations)

> but he's disingenuous in saying that random line duplication can't cause catastrophic problems in Eiffel.

I think you're referring to this part of the article:

  With such a modern language design, the Apple bug could not
  have arisen. A duplicated line is either:
    - A keyword such as end, immediately caught as a syntax
      error.
    - An actual instruction such as an assignment, whose
      duplication causes either no effect or an effect limited to
      the particular case covered by the branch, rather than
      catastrophically disrupting all cases, as in the Apple bug.
To be fair, he's not saying that it can't cause catastrophic problems in Eiffel. In fact, he's not strictly talking about Eiffel but about better constructed languages. He is saying that while it could be cotastrophic, that it wouldn't be nearly as bad as what happened here since it'd be contained to one branch of execution rather than exposed to all branches of execution.
I'm just saying that that distinction is no less catastrophic. It really depends on the line in question, but if a line has side-effects and isn't idempotent, it could be infinitely catastrophic to repeat it unless it causes a syntax error.

Even in the gotofail case, ALL cases were not affected, only the ones after the line. It would be more catastrophic higher in the function and less lower.

BTW, his code sample is NOT what the real bug was -- in the real bug it was more like this

     err = f1(cert) if (err) goto fail;
     err = f2(cert) if (err) goto fail;
     err = f3(cert) if (err) 
       goto fail;
       goto fail;
     err = f4(cert) if (err) goto fail;

     fail:
     cleanup();
     return err; // if this returns 0, the cert is good
The issue is that we jumped to fail with err set to 0 (no error), but we skipped the f4 check. The bug is only a problem if f4 would return an error (not ALL cases)
It seems to me the line of argument here boils down to "bad code can be bad". There's no language that can prevent code that is simply wrong from doing something wrong, not even the proof languages. Even "pure" code will happily generate the wrong value if you "map (+1) ." twice instead of once.

We should instead discuss the affordances the language has for correct and incorrect code. It is not that C is objectively "wrong" to permit an if statement to take an atomic statement, it is that it affords wrong behavior. And the reason I say it affords wrong behavior is no longer any theoretical argument in any direction, but direct, repeated, consistent practical experience from pretty much everybody who makes serious use of C... that is, it is the reality that trumps all theory.

OK, he oversimplified it a little bit: the error checks had side effects, which determined the return value of the enclosing routine.

His point still stands, I think: the code didn't do what was obviously intended, and should have been flagged by the main compiler/interpreter/parser, rather than a supplemental "lint" type tool.

Clojure isn't particularly well suited to avoiding problems like this. I've written a lot of Clojure for work and for open source.

We need pure, typed FP langs like Haskell/Agda/Idris.

To boot, I'm having a much more enjoyable and relaxing time in a Haskell REPL than I was in my Clojure REPL.

Somebody I follow on Twitter just said something apropos:

"girl are you Clojure because when I'm with you I have a misplaced sense of [optimism] about my abilities until I enter the real world"

I don't think clojure (or lisp) was designed to avoid line-duplication errors. It's mostly an accident of it being not very line oriented.

I just randomly picked a function from core to illustrate this, but a lot of clojure code looks similar

     (defn filter
       "Returns a lazy sequence of the items in coll for which
       (pred item) returns true. pred must be free of side-effects."
       {:added "1.0"
        :static true}
       ([pred coll]
        (lazy-seq
         (when-let [s (seq coll)]
           (if (chunked-seq? s)
             (let [c (chunk-first s)
                   size (count c)
                   b (chunk-buffer size)]
               (dotimes [i size]
                   (when (pred (.nth c i))
                     (chunk-append b (.nth c i))))
               (chunk-cons (chunk b) (filter pred (chunk-rest s))))
             (let [f (first s) r (rest s)]
               (if (pred f)
                 (cons f (filter pred r))
                 (filter pred r))))))))
There are very few lines of this function that can be duplicated without causing a syntax error because parens will be unbalanced.

I see two:

In a let with more than two bindings, you could repeat the middle ones. In clojure, this is very likely to be idempotent. In this code, it's the

        size (count c)
In any function call with more than two arguments, if the middle ones are put on their own line, they could be repeated, like this in the final 'if'

        (cons f (filter pred r))
In many cases, you will fail the arity check (for example in this case). If not, the function should fail spectacularly if you run it.

So, I think it's accidentally less likely to have problems with bad merges and accidental edits (not designed to have that property)

(comment deleted)
Your analysis only holds if closing parentheses are all gathered on the same line as final expressions (which may not be true for some styles).
I'm not thinking this through completely, but it seems resilient to a lot of styles.

Function calls (which is a lot of what clojure is) are an open parens to start and very likely not to have that close on the same line (because you are building a tree of subexpressions).

Wherever you put the close (bunched or one per line), if you don't put it on the line with the original open, it will be unbalanced in both spots (meaning the first line and the last line can't be duplicated without causing a syntax error).

True. But consider the form:

    (if (someExpr)
      (doTrueStuff)
    )
Then a duplication of the `doTrueStuff` line would lead to true stuff being done regardless of the truthiness of someExpr (as the third [optional] argument to `if` is the else branch).

This form is not entirely unheard of either. The overtone library for example assigns labels to its event handlers like such:

   (defn eventHandler ( ...
      stuff
   ) :: event_handler_label)
This is actually why I really like `cond` in Common Lisp (and other lisps and languages). You have to make explicit what should happen if your desired expression is true, and the only way to have an `else` clause is `(t ...)` so you have to intentionally create that last wildcard spot.
It is considered unidiomatic in every Lisp I am aware of to orphan parens. To some degree this is a stylistic concern, but it's a much more open-and-shut case than, say, C brace style.
Of course it's just as easy to duplicate a form as a line. When I edit Clojure code I use paredit so I'm editing the structure of the code instead of the text. Instead of accidentally "cut this line then paste twice" I could easily do "cut this form then paste it twice". Paredit will make sure I never have bad syntax but now I have the logically equivalent mistake.
Even in Haskell you can fairly easily get non-reachable code:

  if a == a then
      ...
  else
      -- unreachable
Combine this with the unfortunate habit (which I'm also guilty of) of allowing variables to be called a' and you'll an easy-to-miss bug.
Given that types may define their own instances of Eq, the else clause might be reachable.
And the content of the first block may be unreachable all the time, depending on the implementation of Eq. But you probably have a number of issues to worry about if your implementation of Eq doesn't work for the identity case.
let nan = 0.0/0.0 in nan == nan
I think this guy might be confusing code with style. This is why I never ever let the interns not use braces. We built a style guide that is good at blocking these types of errors. Not to mention that Apple should have tested enough to catch that error... It is about the code, but maybe not in the way this guy thinks.
Few languages that have copied their if statement from C have adopted C's exact syntax. Most, if not all, make the braces mandatory. It is a flaw in the language that it permits such a dangerous style.
Go and Rust make the braces mandatory. Hugely more popular curly-braces languages such as Java, Javascript, or C#, don't.
For this, you don't need a style guide, but a static code analysis tool. This specific error causes unreachable lines and has suspicious indentation.

In my project, that could could have never gotten into production, because the tools would have stopped us, even without a single unit tests.

> This is why I never ever let the interns not use braces.

And hopefully nobody else either.

Beware the "experienced" developer who skips braces "because it's more elegant" or "because it's unnecessary typing".

Another thing that enabled this bug is that it was a language that allows misleading indentation.

Programmers indent all code. By making indentation to be not meanigful in your language you are ignoring large part of how programmers read and write code and allow for many misunderstandings between programmer and compiler.

Is there evidence that misleading indentation had a role in enabling the bug?

You can make a typo, you can duplicate a line, you can misindent a line, you can put a brace in the wrong spot. I believe different languages simply trade forms of potential mistakes for other forms. Ultimately no language will magically fix or make bad code known; it has to be detected somehow. Tests, static analysis, manual review, whatever.

You are right. It wasn't the case for this exact bug. The source of this bug was probably the the design of the code editor that had function that duplicates line bound to a single keyboard shortcut right next to shortcut for saving file that every programmer uses roughly 1000 times a day. (What for? What's wrong with Home, Home, Shift+Down, Ctrl+C, Ctrl+V, Ctrl+V? Do you really need to duplicate single line in place that much so you need single key combination?).

However language that doesn't ignore indentation would make such error benign as duplicating the line wouldn't place code in completely different level of AST.

All programmers indent. Why so many languages happily ignore that?

That's a great question.

I'm a huge fan of the Go compiler's general stubbornness. Have unused imports? Compile error. Have unreferenced vars? Compile error. Perhaps misleading indentation should be another compile error.

Pain the ass? You bet; it's a feature. Ignore the whiny kids who insist their 'flow' is broken by having to insert semicolons. Most software work is maintenance, not new code.

"All programmers indent. Why so many languages happily ignore that?"

I've encountered places where isolated breaks in indentation style radically increased readability. Unfortunately, I can't recall them well enough to reproduce here or know whether I'd now have a better solution. It surprised me at the time.

Two consecutive gotos wouldn't cause a bug if the indentation mattered, it would just be unreachable code.

The programmer is already communicating intention with code style (scope), you might as well design the language to use this signal.

So Python and COBOL are our only options?
Python doesn't allow misleading indentation in the specific case at issue, but does allow misleading indentation (e.g., inside list comprehensions split over multiple lines).
Haskell also features a white-space syntax.
It's optional though, and leaves too much room for ambiguity IMO, which I find annoying since the point of significant white-space is maintaining consistency.

These are all equivalent:

    main = do
      something

    main =
      do
        something

    main =
      do something
And obviously, you can also not use `do` notation.
Code doesn't matter, what matters is what it does.

How usable, secure, stable or fast it is are properties of how well it accomplishes it's task.

There's an amazing presentation by the author of Clojure called Simple Made Easy. Since I couldn't just link people to a 1 hour presentation, I made some notes on it :

http://daemon.co.za/2014/03/simple-and-easy-vocabulary-to-de...

The code that we write he calls a construct, and our application is what he calls the artifact.

We need to evaluate constructs based on the complexity they create in the artifacts.

Using C, for instance, affects the complexity of our applications by introducing many more opportunities for human error.

(comment deleted)
You don't really know that. The brace could've ended up between the two lines and you have the same bug. Been there, done that.
But the problem the author described in the article can be solved just by better programming habit- always add braces even for a one line condition statement.

Or just put the one line statement in the same line with the conditional statement such as: if(condition) statement; so when you try to add a line next time, you will notice it was a one line if statement.

But yeah, not explicitly requiring braces for one line condition statement can give us more succinct code but does requires better programming pratice.

Exactly. And these things can be enforced by style checkers, so you don't even need to have good habits.
> And these things can be enforced by style checkers

At which point you've got a subset of the primary language, and might (if it were available and appropriate) as well be using a language that already had these good style things as language features.

This.
knocte, in case you were wondering why you got downvoted (though by the time you read this you may be back to 1 or higher, who knows), on this forum there's a bit of pressure to avoid the "this" and "+1" comments. Some with high enough karma will downvote these sorts of comments when they come across them. To avoid those downvotes, but still post something, minimally add a sentence or two explaining why you're in agreement. For example:

This. With Ada, for instance, there's SPARK which is a restricted version of Ada with additional constructs (in Ada comments) for formally verifying the program. Spark, then, has grown to the point where it's almost its own language (though Ada compilers can handle it because of how the annotations are placed in comments).

The cost to adopting a style checker (if one exists for your language) is very low.

Even if one doesn't exist, they aren't very complex -- e.g. Facebook thought it was appropriate to create one for C++

https://code.facebook.com/posts/729709347050548/under-the-ho...

The costs are more around build process and compile time. Whereas the costs for wholesale language migration include the risk of not being able to ship or hire.

Style checkers should be exactly that: tools that check code for correct style. If you have "stylistic" choices that are so important that you disallow their usage altogether because they might lead to critical bugs, then what you're really saying is that the language design is broken.

In fact, you're saying that you think that certain parts of the language design are so broken you'd rather work with a subset of the language that doesn't include those features rather than risk having them be misused. This very much puts the choice of language in question.

Not necessarily. "This language is the best available tool for the job. It would be even better if..." That doesn't necessarily make it a bad choice. (It may in fact be a bad choice, but more data is needed.)
Yes -- I admit to saying all of those things. I am also saying, I have other concerns that are more important, so what can I do to mitigate the problems by my language's design?
Programmer's habits are possibly less reliable than requirements enforced by the compiler.

    if (error_of_fifth_kind)
        {goto fail;}
        {goto fail;}
How do braces save you?
That's why you put your opening curly-braces on the same line as the if-statement, like the good lord intended!

    if(expr){
        expr;
    }
You don't allow naked braces either. They have to be tied to control flow.
(comment deleted)
His code example is:

if (error_of_fifth_kind)

    goto fail;

     goto fail;  
if (error_of_sixth_kind)

    goto fail;
The_truly_important_code_handling_non_erroneous_case

My question: If the "truly important code" is really that important, where are the unit tests to verify that it "handles" the "non erroneous case????"

Test. Your. Code.

I agree that it's totally shocking that Apple did not have an example of each kind of bad cert. Even a rudimentary unit test of this code would have caught this bug. I bet they do now.
But rudimentary testing of critical code is not part of Apple's corporate culture. They have released versions of the Finder with amateurish bugs that delete files[1] and versions of Mail that randomly delete messages[2]. Several versions of Mail on iPhone, a couple of iOS versions ago, send hundreds of copies of a message when emailing a link from Safari[3]. They've chosen to hoard over a hundred billion dollars in cash rather than hiring more competent engineers and enforcing quality control.

The blame for these fiascos, and for the goto fail bug, getting out the door lies not with the programmers, who can not avoid making mistakes, but the with the CEO and other management, who decide how to allocate resources.

[1]http://tomkarpik.com/articles/massive-data-loss-bug-in-leopa.... [2]http://discussions.apple.com/thread.jspa?messageID=12758081&.... [3]http://lee-phillips.org/iphoneUpgradeWarning-4-2-1/

The thing that kills me is that rudimentary unit testing of simple functions like this makes development faster. You have to run the code, right? It's easier to run it in a unit test than to set up an environment to run the result.

My pet peeve is code that is so broken that it has obviously never even been run.

> My pet peeve is code that is so broken that it has obviously never even been run.

Yeah...in my first support job I had to deal with a customer call that traced back to a install script for our company's (quite pricey, enterprise back-end) software dying with a syntax error.

Also: just because you can omit braces, doesn't mean you should. Stick to 1TBS and this kind of mistake shouldn't happen.
Agreed, but the author's point (indirectly, perhaps) is that requiring braces is an example of something that should be part of the language, and the fact that it's not should be considered a defect of the language.
This mistake shouldn't happen?

Mistakes always happen. Even if I dedicated myself to using braces everywhere, my mistake might be that a) I put the braces in the wrong place, or b) I forgot to put the braces.

Shouldn't != couldn't. In Haskell and the MLs, for instance, certain classes of mistakes shouldn't happen because of the type system and pattern matching, but a single wildcard could throw that off.
1TBS = One True Brace Style, had to look up that abbreviation [1]. Puts the `{` on the line with the `if`, `for`, `while` text and the `}` on its own line, and requires the braces even for one line blocks. (I knew the rest, just hadn't seen "1TBS" before.)

[1] http://en.wikipedia.org/wiki/Indent_style#Variant:_1TBS

Because Apple doesn't have a software testing culture. At least that's what I've been told by Apple engineers I know who used to work at Microsoft ("software testing is not the same respected discipline you're used to at Microsoft"), as well as when I interviewed with Apple and asked what kind of test team would be backing the product I would be working on (manual testing with no SDETs, apparently).

Take it as the quite anecdotal evidence it is, but to me it explains a lot.

Alternatively use a whitespace sensitive language. Defining how to handle tab vs space is irritating, but there are plenty of solutions to that.
Paleographers have a whole catalog of scribal errors, which can be useful when trying to reconstruct a text from conflicting extant copies. Perhaps it would be helpful to compile such a list of common programming errors, and consider that list when designing a new language. It would include "scribal" errors like Apple's goto or = vs ==, and also low-level logical errors. It seems like this could make a fun paper for any CS grad students out there.
There are such lists, and compilers do generate warnings when you use a construct in it. Most of the problems is with people ignoring warnings.

But then, once in a while you have an example of bugs being inserted because the right construct was one that generated warnings. The Debian SSH bug is quite a clear example.

That's a good point. I'm thinking more about 10-20 categories of language-agnostic error rather than hundreds of language-specific errors. And there may still be scope for a paper that explicitly examines the paleography lists for inspiration. Yay multidisciplinary research! :-) But it's nice to think that such a paper might have applications not just in language design but also compiler warnings, which certainly seems more practical/realistic.
GCC has this, you turn it on by appending "-pedantic -Wall" to the compiler invocation. Sadly, many people don't know about it or care enough to use it.
It's about both!

From a programmer point of view, the ideal is a language that doesn't let you make simple mistakes like the goto fail; bug.

From an engineering point of view, it's having the processes in place to make sure that when such bugs inevitably happen, they don't end up in the final product.

The reality is having both of these things would be ideal.

Yes! It's about creating systems to reduce defects.

Processes are systems; language and its constructs are systems. Both may contain methods to control variation and improve quality.

The point is to control variation. Nothing more. Any complete system that connects what we intend, with we tell the computer to do, with what the computer actually does, is good. We need to move in that direction.

Blaming an individual is pointless. Never was there a more consistent or uncontrollable source of variation than the human. We need to surround ourselves with systems that enforce quality, and that do not let us choose to introduce defects.

For this to take place, we have to first and foremost understand that this is the goal. That quality comes from the system and not the individual. Until then we will see only human error translated to computer error continually and unstoppably.

http://en.wikipedia.org/wiki/W._Edwards_Deming had it.

As the article said. Disasters have more than one cause, almost without exception.

Then, quality minded people just know that they must correct ALL the causes, because they may lead to another disaster in another combination (and that includes dealing with human errors). People without the quality mindset often decide that it's enough to fix ANY of the causes, because that'll avoid a repetition of the disaster.

> "If you want the branch to consist of an atomic instruction, you write that instruction by itself"

No, I don't, I generally use curly braces for those too. So for me, the solution would be throwing an compile error when those are missing. Is that really all it takes to make a language "modern"?

I also don't understand the jab at semicolons, which I like, nor do I see how getting rid of brackets around the condition is really a net saving when you have to write "then" or "loop" instead. Apart from being twice as many characters to type, words distract me (much) more than symbols, and now that I think of it, I wonder how programming would be like with symbols for reserved keywords like "if" and "function", so the only text you see anywhere in your code would be quoted strings or variable/function/class names...

Anyway, I think when talking about distractions, one should be careful with claiming to speak for everybody (at least unless you did a whole lot of studying test subjects or something).

Having worked on both line oriented and free-form languages over the last 30 years, there's less opportunity for foolishness when 1 line = 1 statement. (so long as you can continue a long line with an obvious convention such as a trailing backslash, ampersand or perhaps a comma)

Line oriented syntax need not be limiting: Ruby, for example, does a good job of treating statements such as "if", "while", "case" as expressions that can return values.

Also, there's only one correct way to indent with "END" type keywords: the body is indented to the right of the start/end lines, and the start (FOR/IF/...) and end (END/DONE/...) lines are aligned.

Now if we could just get an "AGAIN (parameters...)" keyword for tail call elimination, coupled with almost mandatory immutability, in more languages... :-)

Even though I already only put one statement in each line, and indent as if indentation mattered, I still like semicolons and curly braces... just to have the option to make the occasional very long freak line more readable without having to change characters because of whitespace changes.
Since he lumps in Java with C and C++ it's worth pointing out that this specific bug is not possible in Java since unreachable code is a compiler error. I assume the same for C#.

Also, many style guides such as Josh Bloch's highly influential 'Effective Java' recommend against the 2-line if statement without curly braces since it's known to be prone to this type of error. His argument that keywords are better than braces for ending blocks is weak.

In C# if this were nested between two Catch blocks (for handling different types of exceptions, per this example) then it would throw a compiler error.

If it were a switch statement, you'd get a warning for code that will never be executed.

I believe it is a warning in C# (have not used it in a while).
I don't think that's quite his argument. His argument is that in Eiffel, the keywords are mandatory, whereas in C, the braces are optional (for a one-line body). And, in fact, mandatory braces would have prevented this particular issue.
> it's worth pointing out that this specific bug is not possible in Java since unreachable code is a compiler error

Not on my Java compiler (Android toolkit and Eclipse). I see it as a warning throughout the code base I work on. :-(

Use IntelliJ (Google's favorite Android IDE - they make a mod of it called Android Studio but the good stuff makes it back to IntelliJ)
I use ADT as well, and IIRC there are certain kinds of unreachable code that won't compile:

  public int foo(int i) {
      return 2;
      
      return i;
  }
On the surface, this reasoning makes sense. Unfortunately, human coders are the ones writing code. This means the code might have bugs in it, regardless of the language you choose. While it would be great to invent a new language n+1 whenever the blame for bad code can be directed at programming language n, it is not likely that you will find coders that are willing to repeatedly take that ride.
How about forbidding newlines in a single-statement if and requiring a newline after the first statement after the if?

So this is allowed:

  if (foo) bar();
  baz()
But this isn't:

  if (foo) bar(); baz()
And this isn't:

  if (foo) 
  bar(); 
  baz()
Edit: formatting
Yes, I was thinking along similar lines. If your coding standard strictly prevents known dangerous habits (and it should be for security related code).

Insisting on putting all if body statements in braces would have prevented the Apple bug.

A pre-processor checking for violations should be possible too.

The heart bleed one seems much less related to language and more about design. The flaw was not spotting that a user supplied value was passed in to a dangerous function. Explicitly denoting what is trusted and what is not could possibly be a feature of a language out there, I don't know, but it's certainly something that could be architected in an existing language.

Put two spaces before your literal code blocks, your newlines are getting eaten up by the formatter.
The formatter ate your indentation. Is this what you meant?

So this is allowed:

    if (foo) bar();

    baz()
But this isn't:

    if (foo)
        bar();
    baz()
And this isn't:

    if (foo) bar(); baz()
Just use curly braces all the time. There's nothing in C or C++ stopping you from using curly braces around single line statements. It's just that you CAN omit them.

But having been bitten by this issue early on, I started always using curly braces. I think it's the better way to write C/C++. Frankly I think those who omit them are just lazy.

I wonder if Eiffel could have gotten a more widespread use, if Bertrand Meyer had gotten a major OS vendor behind it, instead of trying to cater for the enterprise.
I looked at Eiffel seriously in the late nineties. My issue was interoperability and libraries. A little later, he tried Eiffel.NET, but I had moved on by then.

Eiffel's main selling points (safer, purer OO) were eventually credibly implemented in Java and C#, and people who wanted something else, wanted something really different.

Not sure anyone really wanted Eiffel's extensive (and sane) multiple inheritance support. I certainly wanted pre/post conditions and thread-safety.

What attracted me to Eiffel, in the mid nineties, was the whole concept of design by contract, better OO than C++, compilation to native code, GC and belonging to the Pascal family of languages.

What putted me off was Eiffel Studio's price as university student, without knowing if the language would have any industry influence.

Eiffel's contracts are really compelling, I wish there was a reasonable, non-hacky way to implement them in more mainstream languages.
I wouldn't say that clojure is mainstream, but their implementation is reasonable and makes sense in the clojure idiom (just a meta-data map on the function)
Clojure provides a nice equivalent of pre/post conditions in core.contracts, check it out perhaps.
It's clearly true that having a language which forced an explicit ending to an if block would have prevented the goto fail bug.

But is there any actual evidence that code written in modern languages have fewer bugs overall? Or is it all, "let's focus on this one example"?

As another commenter mentioned, the goto fail bug would have been utterly trivially caught by any unit test that even just tested that it reached the non-error case in any way (you don't even need to test the correctness of the non-error code).

I would like to see data before I believe that "errors that would have been prevented by non-C semantics" constitutes a significant fraction of all bugs, or that they aren't just replaced by bugs unique to the semantics of whatever replacement language you're using.

I think it is also a matter of culture.

The modern language communities tend to be more open to static code analysers and unit tests than the C community, even though they have lint since the early days.

To address only the "goto fail" example the author uses, I don't see how the proposed Eiffel solutions are conceptually any different than always using brackets in your C/C++ constructs. Brackets are the mathematical notation for a set, and having a set of instructions inside them makes perfect sense even if the set only has a single element.

Since

  if(condition){ instruction; }
instead of

  if(condition) instruction;
is already considered good practice, couldn't it also be enforced via compiler pragma?
> Brackets are the mathematical notation for a set, and having a set of instructions inside them makes perfect sense even if the set only has a single element.

The thing inside brackets in an if statement is a list of instructions, not a set, so there's no real reason that brackets use in mathematical set notation makes them natural for this use.

A "list of instructions" is simply a "set of instructions" with an order. I was making a conceptual point, not a mathematical one. If you want to be pedantic, the instructions inside the brackets ultimately get converted to opcodes and their numeric arguments, which do form a set.
> A "list of instructions" is simply a "set of instructions" with an order.

No, its not, since an element can be repeated in a list but not in a set. To represent a list of instructions as a set it needs to be something like a set of (position, instruction) pairs -- like lines of code in old-style BASIC, with mandatory line numbers.

> If you want to be pedantic, the instructions inside the brackets ultimately get converted to opcodes and their numeric arguments, which do form a set.

No, they still don't. The "opcodes and arguments" are just instructions in a different language, and are still a list; you can have the same combination of opcode and arguments more than once, and the order still matters.

(If you include the offset in memory at which each instruction will be loaded along with the opcode and arguments making it up, then you'd have position-instruction pairs, and you could represent it as a set. But that's not what you are writing, and the fact that there is an equivalent set representation for something you are writing as a list doesn't make set delimiters natural delimiters for the thing written as a list.)

Yeah. Especially since those brackets are completely unrelated to set theory. An instruction can appear twice in a list of instructions, but not in a set. Also sets are unordered. The reason for the curly bracket choice was that most keyboards had a key for that back then.
You can enforce it at any level. But the deeper you do that, the more people will be protected.

What's the point of having branching construct that uses different syntax for exactly one instruction than for many instructions?

And if you are crazy like that then why not have:

  if(condition);
that does nothing? Oh. Wait. We have that in C, Java (not C# though). I guess consistency is not the be all and end all of language design.
>that does nothing? Oh.

Doesn't that depend on what you consider your condition? createSomething=function that creates something and returns true if correctly created and false otherwise

if(createSomething());

did that do nothing?

edit: perhaps nothing useful since we can't know the state returned by createSomething.

This is one of my most loathed developments in "best practices" lately. I see it everywhere and it just adds noise, since it doesn't change the functionality of the code. It's like adding a comment on every flow control statement. A concrete example is try/catch, which forces the user to add superfluous curly brackets.

In fairness, I have hit the "goto" style of bug he talks about in switch commands, when I forgot to add a break statement. But I can't remember a single time in my programming career when I added a line to an if/for/while and broke something.

I think people are wasting their energies on subjective topics like telling people to use spaces instead of tabs, or putting { on the same line as the flow control statement, or even telling people how much whitespace to use between () and where.

What we really need is a standardized formatter for any language that works like Google's Go Format. Programmers should not be spending time refactoring code just for looks. If anyone knows of a utility that does this and is easily configurable for personal taste, I would sure appreciate it. Being able to convert /* */ comments to // and back again would be a plus.

Formatters are usually language-specific, and there are formatters for most popular languages. See eg. clang-format [1] for C/C++, your IDE's reformat command for Java [2][3], PythonTidy [4] or AutoPep8 [5] for Python, or jsbeautifier [6], JSPretty [7], or Aptana [8] for JS.

[1] http://clang.llvm.org/docs/ClangFormat.html

[2] http://help.eclipse.org/kepler/index.jsp?topic=%2Forg.eclips...

[3] https://www.jetbrains.com/idea/webhelp/reformatting-source-c...

[4] https://pypi.python.org/pypi/PythonTidy/1.22

[5] https://pypi.python.org/pypi/autopep8/

[6] http://jsbeautifier.org/

[7] http://www.jspretty.com/

[8] http://www.aptana.com/products/studio3

>I see it everywhere and it just adds noise, since it doesn't change the functionality of the code.

I'm confused. It sounds like you're saying

    if (condition) { stuff(); other_stuff(); }
is the same as

    if (condition) stuff(); other_stuff();
because the brackets don't change the functionality, but they do. I might be misreading what you're saying though.
What he was saying should be obvious, since there is actual code in the comment he was replying to, and its not what you wrote.
> Brackets are the mathematical notation for a set, and having a set of instructions inside them makes perfect sense even if the set only has a single element.

Then you consider that sets are unordered, and then the analogy doesn't make any sense any longer since the order matters for instructions.

Personally I think brackets are so important in C-like languages because they're such a pain for me to write on my keyboard.

As far as I can tell (and I may not be the best judge of that, because I've used a dozen languages over the past 25 years and still fail to see any really significant difference other than language philosophy as in OO, functional, etc), this is not about language quality but language safety.

Those two are not necessarily the same, and some of the most elegant languages aren't particularly safe. Conversely, unlike people like to claim based on hearsay and no longer existing features, a badly designed language like PHP is definitely not less safe than Python or Ruby.

By the standard for "good" set by the author, no dynamically typed language would make the cut.

blah blah blah acm blah
I am surprised I have not seen anyone mention this yet but a simple regression with line coverage enabled would have caught this. Is it not common in the software community (I do mainly Hardware) to run regularly with line coverage? This seems like a basic problem with process in my opinion.
My experience says it's uncommon. Microsoft seems pretty good about it, and has good tools available, though it might vary a lot from team to team. Smaller companies? I've at times been revered as a testing deity when showing someone code coverage results. Other companies know what the word "coverage" means, but seldom know about or utilize the tools available. They just know they probably ought to do it, which is a step ahead, I guess.

Where I'm currently at, my manager wanted code coverage but needed someone to do it. Since I'm just that guy, I setup the infrastructure for iOS. Android, you're such a pain in the ass to setup for (rooted device, or the dog-slow emulator, and it still doesn't give me results) I'm surprised anyone does code coverage for Android. I can't remember the name of the python tool, but holy smokes it's slick and painless.

So tooling quality varies, which might have a lot to do with the lack of coverage testing. I long for the days of the coverage tool Tim Paterson wrote (yeah, that Tim Paterson). No profiling, it would inject the coverage DLL at runtime. Then, as you used the program, it would log coverage on the fly. So program running on one monitor, coverage log on the other, and start clicking. Can't remember the name of it (it's no longer sold by Paterson Technologies), but damn it was slick.

There are other reasons why choice of language matters. If you need a simple web app, you're probably writing it in PHP or Ruby instead of C. But you'll likely use C if you're interfacing with hardware. A lot of apps that need high concurrency use Erlang. If you can write a quick Python script that solves a problem before you can even get Eclipse to load, then why would you even bother trying to write the solution in Java?

Language errors aside, it's pretty obvious that at least in some cases, the choice of programming language does matter.