Evolving a language in and for the real world: C++ 1991-2006 (2007) [pdf]
stroustrup.com
stroustrup.com
http://www.stroustrup.com/dne.html
It does provide a very clear rationale why C++ became the way it was (C++98), given the design goals and the constraint of being C compatible as much as possible, both in language and compiler toolchains.
Going through all the design decisions, the underlying implementation, the trade offs helped me build a mental model that massively increased my productivity and intellectual happiness.
I yearn for similar works for other languages (e.g., clojure).
http://www.adaic.org/ada-resources/standards/ada83/
Smalltalk (green book),
http://stephane.ducasse.free.fr/FreeBooks/BitsOfHistory/
Cedar,
http://bitsavers.trailing-edge.com/pdf/xerox/parc/techReport...
http://www.amazon.com/Ruby-Programming-Language-David-Flanag...
I think it's the most relevant Ruby book, because it documents the basic thinking and mental approach of the language, which is actually very stable.
That's from his "Design Support Rules" section early on in the paper. I am still going through the paper, but this seems like a good rule and a reason why pointers are still allowed despite the protestations of people who hate C++. Sure, you can get into a pickle with them but that's not the language's fault.
Of course, there has been the rule "use references where you can, pointers where only absolutely necessary" but it's a difficult habit to break...
Arguably, that philosophy is why C++ went on to rule the world, and Lisp and Haskell remain niche languages.
The difference is that Go doesn't believe "useful" is enough justification-- usefulness is balanced against clutter and complexity.
But it is still the same philosophy - Go's design is being driven by real problems that people ran into.
In fact exceptions as implemented in C++ has to be one of my last favourite things about the language. They are an all or nothing proposition and yet were put in half heated and unchecked. Many years later the result I see is some or most programs have a catch(...) at some high level. Something went wrong, who knows what, can't even set the debugger to break at exceptions because they're being thrown everywhere all the time for flow control.
Any C++ debugger worth using will allow you to break on certain exceptions and ignore others. Even gdb permits this: http://sourceware.org/gdb/onlinedocs/gdb/Set-Catchpoints.htm... Any decently designed C++ program will have a sane set of exception classes that will be easy to filter when debugging.
A simple example:
let prod = List.fold_left (fun acc a -> acc * a) 1.0 xs
should not fold over the whole list if a factor is 0.0
so they tend to use exceptions for that (or delimited continuations)The original purpose in ML was definitely control flow. Or rather, controllable heuristic search in the context of a proof assistant. A heuristic could "give up" by raising an exception and a meta heuristic would then try something else or give up.
They worked well because memory handling is clean and automatic in ML and because proof assistants mainly work in their own little world with little to no I/O (apart from logging to a file/stdout/stderr).
When you try to use exceptions, you'll quickly encounter the following:
1. Your C++ code may be called by C code that knows nothing of exceptions.
2. Exceptions have big implications for API design. See for example GOTW #8 http://www.gotw.ca/gotw/008.htm , which acknowledges that "some interfaces can't be implemented in an exception-safe manner."
3. try/catch/throw have performance impact. Compilers optimize for the no-exceptions-thrown case, and the practical cost of throwing an exception can be prohibitively high. Google "Zero-cost Exception" for more.
The net result is that proper exception handling is difficult to incorporate gracefully, and "exceptions are all fatal errors" is a very reasonable position. Last I checked, Firefox is compiled with -fno-exceptions, and this is how Cocoa frameworks work as well.
Go read Sutter's _Exceptional C++_ or any of its follow-on books. It appears nearly impossible to write solid software when you allow exceptions into your set of control structures.
The best response to an exception is: Restart. Catch the exception in something like main(), take a dump or write some diagnostics, and restart your app. You'll have a system that is more reliable and of better quality than if you try to handle exceptions "correctly" in the face of non-exception-neutral code, 3rd party libraries that get things wrong, and your own bugs, which are going to be many, and deep, and subtle, and probably intractable.