Thursday, January 28, 2010

Why are our Programming Languages so Bad?

I just took a quick look at Scala and Lua . . . and I don't think either one is a winner, but for different reasons.

The Scala guys seem to have fallen into the arcane syntax trap - with lots of critical information inferred. I think I agree with this blog post from January, 2008: Scala is probably not a readable language.

Scala seems partially motivated by the Java mistake of believing that 'more required words make more readable code'. That just makes the programs bulkier, not clearer. Other parts of the motivation seem to be to try to find a syntax which supports functional programming, OOP, and everything else which might be fun.

On the other hand, Lua doesn't have a rich enough structure. I think I'd rather write in C than something like Lua. Lua isn't OOP, it isn't a functional language, it doesn't even support structures sufficientlyl, let alone objects. I've had a lot experience working in awk - which is nice, but does not have good support for complex data objects, which makes the programs difficult to maintain and . . . this could get boring fast, so forget it. The bottom line is: languages which don't support a decent object model slow me down too much to bother with.

I've been writing a lot of Python lately - after burying myself in a PHP project for about a year and a quarter. I also write a lot of shell script, HTML, CSS, and whatever. I don't write Ruby anymore - but I don't want to get into that now. Before this I wrote a lot of C, Pascal, awk, etc and - believe it or not - about a half a ton of FORTRAN. So I've written a lot of code in some pretty awful languages.

The simple fact is that none of the modern languages are any good. They all suck.

Why is that?

We really know a lot about language design by now - or at least we should.

One of the things which really irritates me is that every damned language uses different syntax for the same semantic concept. I don't think I know of two languages which implement if ... else-if ... else ... the same way. [else-if is spelled 'elif', 'elsif', 'elseif', or 'else if' or doesn't exist].

It is now a fact that programmers work in multiple languages. These stupid, unnecessary syntax inconsistencies make life hell.

I'm starting a list of what should be in a modern language:
  1. It should be syntactically as small and clear. Don't use words where symbols are clear: i.e. don't use 'begin' and 'end' - curly braces work just fine.
  2. No reserved words. We don't need them. Somebody - I think it was Kernighan - pointed out that a decent parser can determine the meaning of a word based on the structure of the sentence, so that 'for' can mean one thing in one context and something else in another.
  3. It has to support objects with (at least) single inheritance. I think Ruby got that mostly right. I think Python got it wrong by supporting multiple inheritance. I like Ruby's idea of Modules because it allows excellent code reuse. It gets us the utility inheritance promises without the head aches.
  4. Scoping has to be correct from the start. Block scoping the way C does it is right. Matz got that wrong in Ruby - I hear they're changing it again in 1.9. Python still doesn't have it right yet, but I think it's getting closer.
  5. Global variables are Evil. Crockford is right: They should not exist.
  6. Variable declarations are a pain - but less painful than tracking down spelling errors which the language accepts. I've lost a lot of time needlessly hunting down misspelled words in PHP that would have been caught by simply requiring variable declarations so the compiler could catch them. So, variable declarations are Good.
  7. String handling is Good. If you don't think it's a necessity - go write some string handling in C for a few years.
  8. Dynamic - aka Duck - Typing is Good - we need it. I wasted too many years living without polymorphic stuff to ever go back [read: writing in C, pass a pointer and figure out what it is inside the function and hope to hell you don't screw up].
  9. Static Typing is Good - we need it too. I've wasted too many years finding bugs which could be caught by a decent type system.
  10. Functions need to be 1st class things. In fact, everything needs to be.
  11. Closures are Good - we need them.
  12. Functional programming is good - We need it.
  13. Imperative Programming - Structured style [like Dykstra told us] is good - we need it too.
  14. Operation overloading is Good - We need it. Everything should be overloadable. I think Ruby got that right as well. Python has been incrementally getting there for years.
  15. Self modifying code can be a good thing - but it's hard to do right and rarely needed. I think it's better to support it directly rather than trying to discourage it. This is in spite of the crap the Ruby community loves to write [they call it meta-programming, but it's not really meta programming - it's automatically generated code] For some reason they think that self-modifying, self-generating code is intrinsically a good thing - but then I used to do a lot of stupid stuff when I was younger too. I think this is a result of lack of experience - especially in maintenance - and a lot of incompetence. It sure makes Rails a mess.
  16. Exceptions are good - We need them. Error handling is always an issue and good, clean exception generation and handling support makes it easy to include it.
  17. Interpreted is Good. It makes writing code much faster. Must have a REPL.
  18. Compiled is Good. We always need speed. Only the stupid say 'speed doesn't matter'. It always matters, but very rarely at the expense of clarity.
  19. Do we need an IDE? I don't think so, but I don't know. I just write in TextMate on my Mac. I used to write in Emacs - and still use vi from time to time. I've tried Eclipse, but just couldn't deal with it. I think this is a non-issue except that the Language should support development without and IDE.
  20. Reflection - aka inspection - aka whatever - is Good. Python has that pretty right. All functions and classes take an optional documentation string which you can print in the interpreter by typing print foo.__doc__. It saves lots and lots of time paging through documentation. There is also a builtin function called dir() which generates a sorted list of all the attributes of it's argument. Ruby has that wrong - I don't know how many times I wrote foo.methods.sort to try to remember the name of a method when hacking Ruby.
  21. Automatic Document Generation is Good - but the current systems stink. They are like a tail wagging a dog: Code is always more important that comments - and all documentation is comments. Why? Comments don't execute, so they always have bugs and eventually diverge from the meaning of the code [See Brooks: Mythical Man Month where he points out that it's not possible to keep to separately maintained files in sync] Documentation needs to be unobtrusive, compact, and easy. I have no idea how to do this - yet.
  22. To be Continued
Any Things to add to the list?

4 comments:

PHP Developer India said...

In JavaScript, things are not quite so bad. There is only one numeric type..

Mike Howard said...

Read Crockford's book: Javascript: the Good Parts. Javascript is a mess.

Personally, the thing I like least about Javascript is prototypal inheritance. It allows OOP, but doesn't support it cleanly.

José Ruiz said...

I don't get your point... Why MUST different languages have the same syntax? Syntax and semantics are very important to a language. If they can't change, then there is no need for more than one language.

However, thanks to the diversity,we have powerful (and horrible) Perl syntax, minimialist, elegant and powerful Lisp (which allows to treat code and data as equals), etc. The syntax is a core part of language design!

Mike Howard said...

My main issue is with syntactic variation which does not add conceptual value. I agree completely with you that syntax needs to evolve - but we already know a lot of syntax that works well - as well as a lot that doesn't.

Perl - for example - has demonstrated that prefix characters don't work. [forgiving the type-by-prefix nonsense, it also broke recursive data structure definitions (as I vaguely recall)]

Gripe 1: variation w/o adding concepts

Syntax variations for key words which do the same thing: e.g. elif, elsif, elseif, else if & end, fi, end if.

These variations do not contribute to progress in language design. if .. elif .. else .. end isn' new, so any language which is key-word based isn't adding anything new conceptually when changing the spelling of one of these words.

Same comments hold for `switch', `case', ...

That said, if there are valid syntactic reasons for using a different form - (if (s-exp) (s-exp) (s-exp)) - then, that's fine. But otherwise it's simply gratuitous.

Gripe 2: Arcane-ity.

Some language designers seem to think that people who use their language will all take the time to learn it thoroughly and - if not to the exclusion of everything else, then almost everything else.

[sidebar - did you know that it used to be legal to embed paces in FORTRAN variable names? It probably still is, but I don't even want to know

IF (FOO .EQ. F O O) WRITE(1, 6H HELLO)

should write out HELLO, left justified w/o carriage return (but maybe not - it's been a looooonnnnggg time) ]

Almost all languages need to have a small core which allows experienced programmers to become immediately effective and which obviate the need for arcane constructions with significant implied consequences.

I'd like to give an example of what I mean by arcane syntax, but can't right now. Scala comes to mind, but I all I can remember about it is throwing the book across the room in a tantrum when I read another non-obvious syntactic construction which was supposed to make things 'easier'. (but I may have been in a bad mood and been wrong. maybe Scala has consistent syntax and doesn't have a lot of abbreviations and variations which make it impossible to read someone else's code without knowing all of Scala)

Things are hard enough in parsing code which is heavily dependent on evaluation order based on precedence tables.

A Ruby example: (I don't consider this arcane syntax. The construction is rare enough that it is obscure - but that is a matter of familiarity rather than design)

def a ; puts 'a' ; end
def b ; puts 'b'; end

z = if x = a || b or y = 3 then 'pong' else 'boing' end

prints 'a' & 'b', then assigns 'nil' to x, 3 to y and 'pong' to z.

(inspired from a slightly more complex expression I found buried in Rails 3.0. It actually makes sense in that context from an efficiency point of view)


Thanks for the comment.