Saturday, October 5, 2013

A reply to Verity Stob

Imagine my pleasure to be mentioned by the one, the only, the legend, Verity Stob.   If you are not familiar with the work of this renowned pundit, journalist, and composer of postmodern satirical brilliance, since 1988, you can find her writing these days over at The Register.  She mentioned me, and my somewhat vitriolic C++ post, back in June, when I said that the great thing about Delphi is that it is not C++.  Harsh? Yes.  Rediculous? Maybe.  Truth underneath? I believe so.

As with most of my more hyperbolic statements, I wonder if I must clarify further exactly what  ridiculous position I was taking, though.  I should very much dislike to be heard to be saying any more or any less than I meant to say by that. And as happens, time goes by and my position moderates or moves, as more information, and more thoughts pass through my gray matter.

What was my "most ridiculous position"? My assertion that what is great about Delphi is that it is not C++.  I still maintain that this is true, but the ire with which my original statement was made is something that I have come to regret.  My apology in this post is to C++, though as I don't think I offended Verity, rather I suspect my C++ slam was amusing.

Verity speaketh verities, yea verily. For indeed, Shared Memory is an intrinsically tricky proposition, and I am not aware of a Shared Memory library for Delphi that is free of accidental complexity, nor of any library of the scope and feature set that Boost has.  So far, Verity, thou hast said rightly.

Further, there are in fact multiple options in the C++ world.  The Boost shared memory library is among the most complex, and most difficult to understand libraries within the boost framework, and there are several simpler non-template-laden C++ library options for shared memory.

But still, I see a general pattern that in C++ everything is more complex than it had to be, and yet, I do not blame C++ anymore, nor the developers.  Have I changed my mind that both higher levels of essential and accidental complexity are baked right in there?  No.

My recent epiphany, thanks mostly to Chandler Carruth, is that it's not C++'s fault.

 I will try to explain what I now think, and why I now think it, in a series of linked logical propositions, each of which is open to attack, but I only ask that my propositions be considered in detail. If they are ridiculous, you would do me a favour by pointing it out as I value logic and reason above ego and dogma.

  1. Because template meta-programming (in C++) and generic programming (in Delphi, C#, and Java) is higher order work, it is harder to understand, harder to learn, and harder to maintain. As a language's powers grow, so do the scope and number of ways you can abuse them.  The very things that make both C++ and Delphi powerful and useful are also the source of the greatest problems that arise from both their use.
  2. C++ is more powerful than Delphi, and is thus, harder to use. Because C++'s power comes along with a history and a series of now-unchangeable-decisions, this accidental complexity is already higher than the accidental complexity of a less convoluted, less powerful language like Delphi. You can dig deeper holes in C++ than you can in Delphi, and you can build higher towers. Most of us are building apartment flats, though, not the Burj Dubai.
  3. If you don't need that much complexity, you're better off leaving it off the table.  C++ programmers often reach this point themselves, when they start advocating subsets of C++ that are "allowable".  By being a non-templated, non-multiple inheritance modern OOP language, Delphi (and Objective-C) have some advantages (as well as some limitations) with respect to C++.  Delphi is not C++, and is both better, and more limited than C++, depending on what you want to do. For most of what I want to do, Delphi is better than C++.
My evidence for the above propositions is as follows.

A. Watch this illuminating talk  The Care and Feeding of C++ Dragons by the very talented Chandler Carruth, a Google employee, who works on CLANG/LLVM, giving a talk at Microsoft's Native 2012 summit.    If you are a Delphi-centric developer and you haven't encountered the terms "Undefined Behaviour" or "Almost-Strong Memory Model" before, you might want to google those and have their meanings fresh in your mind before watching. Ask yourself,  if C++ really is as "expert friendly" as Bjarne says it is, is that a good thing? The C++ community is aware of this problem, and is trying to avoid framing it as frankly as it could be framed.  The talk spends a while on whitespace and formatting.  Then it goes on to explore the other tools that Chandler's colleagues have been building, and why.  It's fascinating.  Chandler makes an aside about C++'s complexity not being C++s fault.  In the context of that talk, I was enlightened.  He's right. I forgive you C++. But you're still harboring lots of Dragons.

B.  Bjarne Stroustrup, father of C++, a guy I admire very much, and ask yourself what he means when he says C is a gun you can shoot yourself in the foot with, but C++ is a machine gun that can blow your leg off.  I have his new book, and I'm really enjoying C++ 11 so far, but the in-the-field reality is that the life of a C++ programmer is one of working in the trenches of ugly C++98 code.  


C. The allowable-subsets-of-C++ that I have seen so far, and the efforts I have seen to improve the accidental complexity of C++ include, but are not limited to:

Ironclad C++  - A paper and proposition on a subset of C++

Embedded C++ - A subset of C++ that removes exceptions, multiple inheritance, and more.

MISRA C++  - Advisory rule system (static) to try to help you write safer C++

Clang Analyzer - A fantastic looking static analysis tool that adds more warnings when it detects you may be shooting yourself in the foot.  A bit of a demo is included in the Chandler Carruth demo above.

The Java Language and the C# Language   -  The complexity of manual memory management (a fault C++ shares with Delphi, but to a greater extent) and the other perceived or real flaws of C++ were a key motivation in the creation of Java, and the later creation of C#, which was itself a Microsoft reaction to its feeling miffed about being told it could not Embrace, Extend, and Extinguish Java by making its own proprietary JVM extensions.   C# is in many ways, the most beautiful language I have ever seen, with most of the warts of the other Curly Brace Languages (including Java)  fixed in a lovely way.   I do not have a lot of love for non-native-runtimes (JVM, .NET runtime) but as a language, C# is surely one of the most beautiful Statically Typed Languages ever designed or built.

C++ Holy Wars - If I might be allowed one mostly personal, anecdotal addition, it's that every time I've worked with other C++ developers, the usual holy wars about indentation and library use are usually usurped and over-arched by conversations about which C++ features are allowed in this codebase, and which are not.   Some developers in some places hate the ternary ( a ? b : c) operator, some hate template metaprogramming (I'm getting close to that point myself),  some hate smart-pointers and shared-pointers and avoid them, except as a last resort. This includes Bjarne Stroustrup.

Now let me knock a few obvious holes in the above evidence, so you won't think I'm a complete Delphi-loving idiot. 

  • There are more tools, libraries and attempts to improve C++ than there are for Delphi because more people use C++, not because Delphi is less complex.  I know this.   I am not arguing on the existence of some cardinal number of different tools that any link should be made between the complexity of C++ and Delphi.   That, frankly, is easy to do. Compare the complexity of the grammar of Object-Pascal and the grammar of C++, be sure to include the vast differences between the C family language style (separate compiler and linker and preprocessor stages) and the Delphi/Pascal language's simpler compiler and linker.

  • By complexity in C++ I do not mean only accidental complexity, but also essential complexity. These two types of complexity are both going to add to the cost of software development.  But as Chandler Carruth points out, it's unfair of me to target C++ as being the cause of most of the complexity that I deal with. Nevertheless, due to the real nature of the problem (the essential complexity that C++ was designed to help you deal with), you still are going to have that weight on your shoulders.  Just please, let's not villainize the wonderful, smart, and amazing people who have designed, and built C++. They were not out to get you. They have the same universe to live in that you and I have to live in.  They do not wake up in the morning and dream of new ways to terrorize the rank and file programmers of the world with their new language misfeatures.  If anything, they got into this work (designing languages, implementing compilers, working on ISO language standards) to tame the very dragons of complexity that I curse, when I bemoan my ill-luck debugging template issues. 
  • The context in which I made my original blog post, was while trying to modernize an old C++ codebase, and bring it up from Visual C++ 6, with it's well known standard library bugs and ancient no-standard-but -itself status, to a modern Visual C++ version.    This codebase included a very old and very sad shared memory implementation that was based around mapping a block of shared memory, and resizing it when it caught access violations happening. This code had no unit tests, this code was based on libraries for which not all the source was present (some LIB files for Visual C++ 6 contained classes that no source code was present for), and a host of additional issues.   In the ensuing attempts to rescue this project from obsolescence, I was learning Boost, learning modern C++, and (as is usual for me), assuming the fault lay with other people, and other things (like C++) instead of with myself.    The bugs I found in the Boost Shared Memory library were mostly a result of me misusing the library, and the result of mixing that library with a quite insane and crusty codebase.   I ranted, and I raved, I even posted a message to the Boost mailing list that I am now ashamed of.   The author of the Boost Shared Memory library is a better developer than me, better, smarter, and more humble. Mea culpa. I'm an idiot sometimes.   But even with that admission (that casts some serious doubts upon my original assertion about C++), I still maintain that the job of working in C++ is harder than the job of working in ObjectPascal, and that part of Delphi's advantage as a "Force Multiplier" for Developers, would actually be eroded, if Delphi ever achieved feature parity with C++.

 And in conclusion I'd like to say that Delphi basically allows someone of mere mortal capabilities, to get a job done that would require a Ph.D.-like grasp of compiler and language semantics, in C++.   This is the key challenge that C++ faces in the next 20 years. It is not going away, C++ is far more important and far more powerful than Delphi.  In a way, I admire the beautiful new language that C++ 11 and C++ 13 are trying to become.  But the legacy of C, the ill-advised choices in C++98, and even, the odd codebase still stuck on Visual C++ 6.0 are part of the panoply of things that a  "C++ Programmer" today, and in 2023, will still be stuck dealing with.  I for one, am very sad that Delphi jobs are drying up.  Because any Delphi developer's next job just might be a C++ job, or a mix of C++ and Delphi, I think it's wise if Delphi geeks know both.  I'm not saying I'm a Big C++ fan now.  But I'll try to grin and bear it with better humor than I had previously managed.  Because I'm still learning and growing as a human being, I ask anyone who is offended by my C++ trolling to please forgive me.  Including Verity Stob, who I hope, might see that I'm not quite making as many ridiculous claims now that I have had a bit of time to reflect.  Because, hey, Verity, you were right.   I was being a bit ridiculous.  If I have erred in my analysis above, I trust the mighty internet to correct me in the comment box below.



TLDR?  There's no such thing as a free lunch.








7 comments:

  1. About C# (which I use on a daily basis in addition to Delphi), I would like to say that it is a beautiful language... but I'm still so happy when I stay in Delphi. For instance, I would not use C# without the "resharper" tool, which is able to convert to/from C#/LINQ (yes, to my understanding, LINQ is a new language within C# - a statically typed and inverted SQL query language).
    I'm pretty happy with C#, and feel sometimes sorry for "young" programers how feel sorry about Delphi ('is it still existing?"), and would have code the same exact way in Delphi than in C# (inefficient code which do the work but is slow, memory consuming and unmaintainable) - with the benefit of C# of having a GC - so at least they would not create memory leaks so easier as with Delphi. :)
    I do not like the latest "javaish/Csharpish" extensions of Delphi - especially in its RTL.
    FPC in this context is more conservative, and also more "open minded" - e.g. with the "objective pascal" mode.
    For me, Delphi is just a perfect fit for the average programmer.
    See http://blog.synopse.info/post/2013/03/26/Delphi-is-just-a-perfect-fit-for-the-average-programmer

    ReplyDelete
    Replies
    1. I forgot to mention some points which are missing in C# for me on a daily basis:

      - enumerations and sets in Delphi are much more powerful than in C# - I like to use those, and e.g. use them as indexes for constant arrays - which is much better than using a dictionary at runtime

      - good old "object" type - which allows stack-allocated OOP in addition to the main heap allocator - new languages like http://en.wikipedia.org/wiki/Rust_(programming_language) have several object allocation schemes, whereas C# as only one (structs are not objects)
      IMHO deprecating "object" in Delphi was just a big mistake, and lack of preparing the future, where everything is concurrent and multi threaded

      - pointers are not evil when used with string typing, and can be incredibly powerful

      - COW strings and other structures (like dynamic arrays) in Delphi are SO powerful when working with text or data... using a stringbuilder is so boring and counter/perform (immutable strings are a true wrong idea)

      - a true "TMyClass = class of TMy" syntax which works and is very powerful!

      - no runtime, no DLL, no prerequisites...

      Delete
    2. Note: the good old object type is similar to the advanced record type. So it's possible to use the advanced records instead of the old object types in many cases.

      Delete
    3. Record does not allow inheritance...
      Nothing to do with oop!

      I understand virtual methods are not mandatory.
      But IMHO inheritance is.

      Delete
  2. Java got proper enum support in 2009 I think, and Python 3.4 added them just recently. Maybe C# will soon too?

    ReplyDelete
  3. In my own view, the decision to contain C as a subset was horrible. I fully understand the reasoning behind the choice, but it was an opportunity thrown away. C has some really ugly irregularities. A syntax even its mother would have difficulty loving. Much of what is best in the language came directly from its foundation on Gordon Bell's CPU architecture, which was regular and elegant. For the less wonderful parts, full marks to K&R. Making that mess a core element in C++ left some ugly scars.

    As to the readability of C++, I have struggled to read some pretty ugly C++ source (and some equally ugly Delphi source.) I have also written some (I think) very readable C++. I did it in C++Builder, and by writing it much as I would Delphi. The C++ syntax is not (entirely) ugly. Rather, some of the worst C++, I think, is written by old C hands, who will never leave behind the quirky features of the underlying C language. Also, there is a cultural legacy in C of writing incredibly terse code. In any language, that does not lend itself to readability.

    Finally, once C++ was given over to a committee, all bets were off. At least some elements were doomed by that to resemble a camel.

    ReplyDelete
  4. Not only was C a subset, the first C++ 1.0 implementation was a preprocessor layer that generated Pre-ANSI C code of the sort that was common at the time. Bjarne implemented the C++ preprocessor to output C code, and did not build a whole compiler. Given his resources and time, and the task of starting from scratch, it was a smart move. Once it was established as a precedent, it became a law for C++ from that point forward, because the existing Corpus of valid C++ code could not be "just broken" at a whim.

    ReplyDelete