Sunday, January 16, 2011

Different complexities: Lisp and C++

I could not think to two more different languages than C++ and Lisp. They are different in almost all relevant aspects. Even the underlying philosophies are completely different. The culture clash between Lispers and C++ users is well epitomized by the famous (perhaps notorious) post of the great Naggum.

Some quotes from one the post are:
  • C++ is a language strongly optimized for liars and people who go by guesswork and ignorance. 
  • ... it's just that in C++ and the like, you don't trust anybody, and in CLOS you basically trust everybody. The practical result is that thieves and bums use C++ and nice people use CLOS.
And a quote I would have missed were not for wikiquote is
  • I believe C++ instills fear in programmers, fear that the interaction of some details causes unpredictable results. Its unmanageable complexity has spawned more fear-preventing tools than any other language, but the solution should have been to create and use a language that does not overload the whole goddamn human brain with irrelevant details. 
Now, Naggum's post are usually extreme and it is easy to get offended. However, were not for the post I linked (and for a certain Python guru which introduced me to Python) I could probably have my mind crippled by C++. Not that C++ cripples the mind; exclusive exposure to just one language and strong belief that it is the best in the world[0] does it.

Dropping Naggum's extremes, it is true that C++ is about coercion and mistrust as Lisp (and Python) are about freedom and trust. Notice that supporter of different languages disagree on whether freedom is a good thing, after all. Ask an Ada programmer and he'll have you jailed just for thinking about certain things.

Then I asked myself: there is something C++ and Lisp share? And yes, there is. They have huge manuals. I'm quite a Lisp noob and for certain I miss many opportunities. No single course could cover all the stuff (the reference is some eight hundred pages alone!). Every time I look at some article, different book or manual, I usually find some feature I did not know about (and which usually I like it a lot).

I consider myself rather skilled in C++ and, although it is the single language I forget more easily[1] when I do not use it a lot[2]. However, I often speak with inexperienced C++ programmers and I find out they often are missing lots of language and library features.

There is a difference, though. My Lisp code is correct. Perhaps some experienced Lisp hacker could argue that not using some feature makes the code longer or less readable, and I agree, being a fan of idiomatic code. However, the code is not wrong, neither is "Python in Lisp" or something like that. I know the functional model quite well and I can always apply it.

In other words, my inexperience is shown in choosing a longer route, not a wrong one. On the other hand, from my experience, inexperienced C++ programmers just write wrong and inefficient[3] programs; but let us ignore the inefficient thing. They are wrong or in the best situation just error prone. Since it is very hard to design interfaces correctly, I often see implementation leak in the interface or internal handler somewhat exposed (which basically blows the whole encapsulation thing and disables the painful private/public discipline with an orange toxic fart). And even if they are exposed correctly (e.g., iterators) they are always risky. I believe every C++ programmer (included myself) has at least once used an iterator which has been invalidated and had nightmares about it ever since.

Ignoring safe pointers (auto_ptr, shared_ptr) is a good way to write buggy software. In fact, auto_ptr is also a good way to write buggy software: it's semantic is just quite counter intuitive. On the other hand, shared_ptr has hidden costs and should be used with care. Then there is scoped_ptr... which embraces fully C++ design philosophy: if you are using it wrong (copying it) you won't even compile. In a certain sense the concept of "reference" to an object is expressed in 5 slightly different ways (7, if we put the things to hold arrays as well). Not using them correctly leads to incorrect or bug prone code.

Basically, Meyers/Sutter's excellent books are not there to improve C++ code in the sense that programmers can live without them and simply write less enlightened code. If they don't read[4] them (or similar books) their C++ is going to be a vivarium for bugs. Perhaps if some of them is very clever and very experienced (in other languages), she may just see them coming and "rediscover" some of the recipes all alone. Ok, that's fine, just harder. On the other hand in Lisp we are just "missing" the tenth consecutive cool feature.


----
[0] this applies to Lisp and Python as well
[1] this means something, doesn't it?
[2] and I'm happy about it
[3] I believe that if you use a language that traded everything, common sense included, for speed and you write slow code, you have seriously missed the point.
[4] I take for granted the whole read/understand thing

No comments: