What I Hate About Your Programming Language
by chromatic05/12/2003
Author's note: based on voluminous comments, I've clarified a few places where my original intent was unclear. Thank you all for the feedback!
At a very high level, all programming languages are similar. They all require you to describe a problem to solve. They all require similar skill sets — a good programmer in one language will find his or her skills will transfer to another language.
Of course, at a practical level, there are important differences between languages. Different language families make certain techniques easier than others. You'd rarely be able to write a useful procedural program without any variables, but it's common in functional languages. Expressing a logic problem in terms of objects and classes will require a different approach than that of an imperative language.
Setting aside those differences and looking at the popular languages for open source development today, there are many syntactic and otherwise superficial differences. At one level, all languages have a philosophical axe to grind. They exist for a reason. At another level, they're all just flipping bits and jumping around in a long chain of ones and zeroes.
Everything in between is a matter of taste, and that's where most of the messy details go.
Language Philosophy
The philosophy of the language designer shapes the language, its libraries, its ecological niche, and its community. Pascal was a teaching language. C is portable assembly. Lisp syntax is very much s-expressions. Perl is expressive glue.
Consider what happens when someone proposes a great new language feature. There are several possibilities; the language designer may reject it outright, saying that it doesn't fit in the language. (That leaves the door open for someone to write or modify the compiler or parser to support the feature.) The designer might include it in the language by adding a new keyword or by making some other change to the core functionality. The designer might suggest it's better made available through some extension mechanism, such as being added to a core library or an optional library.
Of course, a language with general-purpose features has a way of escaping its original ecological niche. That's why some people use PHP on the command line, server-side JavaScript, and even C for network applications.
To summarize, languages often embody a philosophy. If successful, they're used in domains that the original designer may not have intended. They're often extended in ways the original designer may not have intended. (That's probably a mark of success.) Do these facts suggest any way to evaluate a language's goodness?
Language Goodness
Deciding a language is "good" or not is largely a matter of personal taste.
I prefer dynamic typing and don't care much for the static typing found in C
and its descendants. Part of that is personal taste — it feels like
premature optimization to decide that a variable will always fit into eight or
16 bits. (Yes, I know judicious typedefs can lower some of the
maintenance costs.) It's also practical; I don't write a lot of code that
would benefit from static checking. (A buddy writes drivers at a semiconductor
company. Static checking saves him time and effort.)
Barring the issue of personal preference, we're still left with rather
subjective criteria. Within a domain, it's worth analyzing a language by how
well it solves your problem. I'd hate to write a CGI application in straight
vanilla C; I'd go crazy trying to manipulate strings. On the other hand, there
are amazingly accomplished C hackers writing web programs like
mod_virgule. Familiarity with and fluency in a language are very
important.
It's also worth asking how often the language gets in your way. I once wrote a program to send out update notifications when a web page had changed. My original program was a 10-line shell script running on a Unix box. For various reasons, the final version was a couple of hundred lines of Java running on a Windows machine. Granted, my Java wasn't so hot, but the shell script just glued together existing Unix commands.
Another good question to ask is how well the language supports writing good and maintainable programs. I don't really mean that a language that enforces literate programming is necessarily better than a language that doesn't require you to declare variables. On the other hand, support for lexical variables, encapsulation, reusable components, and other good programming techniques certainly make it easier to keep using a program. This can be taken too far; a language that could prevent really, really bad programmers from doing really, really stupid things probably wouldn't be useful for much else.
It's reasonable to consider a language's supplementary virtues. Can you imagine programming in Smalltalk without using the browser? Would anyone use Java without the class libraries? I suspect if I got used to a good IDE, I wouldn't notice the pain of a strong static language as much — that's worth keeping in mind. Where would Perl be without the CPAN? Can a good user community keep people using an otherwise mediocre language? You bet.
Beyond these criteria, it's really hard to come up with a set of guidelines that make a good language. It's hard to judge the suitability of a language without practical experience in it — where pointer arithmetic is familiar to a C hacker when manipulating arrays, it's terribly unidiomatic in a language with queues and stacks as available data structures.
Besides that, the general purpose languages are all reasonably equivalent. It's not difficult to solve many of the same kinds of problems in any language. Given a good set of libraries, some experience using them, and a good IDE if necessary, a competent programmer could write an equivalent program in several different languages with roughly the same amount of work. Consider how many of the programs written today do one of the following:
- Manage a web site.
- Control a small GUI application.
- Put stuff in a database, process it, display it, and update it.
Even for more difficult tasks, decent languages tend to allow you to use existing libraries for trickier tasks like very fast XML parsing. C is really the language of the Empire, for better or worse. If there's a C library that does what you want and the language lets you use it, you're at least halfway there.
What's left? It's largely a matter of taste. That's where the friction comes in. Maybe personal taste is a poor reason for choosing a language, but it's a major reason for creating a new language. Language philosophy is subjective enough that it might as well be personal taste. Besides that, the first things a new user will notice about a language are syntax issues — where the language philosophy is most evident.
Language Badnesses
Of course, choosing any one language for a project often means not choosing another language; sometimes that's the most compelling reason. I've had projects where a manager said, "We're going to use language X because that's what VCs expect us to use," where language X was, in my opinion, the wrong choice.
Going further, differences in language philosophy present a barrier to learning new languages. This doesn't just apply to existing programmers. It also applies to new programmers — learning to think like a programmer is difficult enough without bothering with syntax issues, editors, compilers, linkers, and distribution.
In the spirit of demonstrating language subjectivity, here are several of my opinions on popular programming languages I've used and how I feel about them. They're completely subjective. They don't bother everyone. What I dislike about one language may be something you love. That's fine.
What I Hate About Perl
The syntax for working with anonymous data structures and structures by reference is ugly:
use constant HACKERS => 2; my $count = keys %{ $self->{groups}[HACKERS] };Writing extensions in C can be Byzantine. Perl's internals are full of macros. Sometimes they make life easier; other times, they hide a lot of complexity that you really should know. XS is a hybrid of Perl macros, Perl, and C, and it can get quite complex. Inline can't always protect you from that.
What I Hate About Python
The special case syntax for constructing a one-element tuple has always bothered me; the trailing comma seems unPythonic.
Half-hearted closure support means that enclosed lexicals cannot be modified. I could emulate real closure support with objects, but sometimes the functional solution feels more natural.
The whitespace issue isn't a big deal (I use a good text editor), but I've spent too long debugging make files to be comfortable with invisible-yet-syntactically-significant characters. Python does have the advantage that it doesn't dictate how much whitespace to use, but I'm still leery of issues with copying and pasting code from a web browser or a newsreader.
What I Hate About Ruby
The sigils that mark instance and class variables always stick out visually in an otherwise clean language.
The available English documentation is still exceedingly slim, compared to Perl and Python.
What I Hate About PHP
I seem to run into lots of special-cased features. For example, I always seem to try
new dir( $path )instead ofdir( $path ).There's no namespace support yet, and I've never liked name mangling to get around this.
What I Hate About Java
The inconsistencies between what the language allows and what the standard library actually does bother me. If operator overloading is so bad, why does the
Stringclass do it?Interfaces get around part of the lack of multiple inheritance, but I'd like to be able to reuse common code in ways besides inheritance. Mixins that don't require inheritance would be a nice touch.
The libraries and the interpreter aren't cleanly separated. There are ways (involving decompilers), for example, to get regex support in 1.3, but I'd prefer to upgrade the standard library and the interpreter separately sometimes, rather than in one big chunk.
I like the idea of checked exceptions in some situations, but forcing every method to deal with (catching or throwing) all exceptions that its child calls or may call can be tedious. I'd rather be able to ignore an exception and let it propagate upwards. Sometimes, I'd rather not worry about exceptions at all.
What I Hate About C
It's often used inappropriately. Unless I'm writing a kernel, a device driver, a virtual machine, or an interface to a C or C++ library, writing in C is a probably premature optimization.
Thirty years of growth and patches to the standard library have produced many, many similar functions with similar, terse names. Quick, Unix coders, what are the differences between
execl,execlp,execle,execv, andexecvp? If you're not using these every day, you'll be hittingman 3 execwhenever you see them.
What I Hate About C++
I find the template syntax ugly. I have no idea how to make it more beautiful, though.
It feels funny to add code that does nothing to prevent the compiler from helpfully adding code that does the wrong thing. Consider the default copy constructor, for example.
What I Hate About JavaScript
Okay, I really don't hate very much about JavaScript. It's pretty good for manipulating DOM elements, and I really like bookmarklets. Working around browser bugs and incompatibilities scarred me for life, though.
What I Hate About XSLT
I'm familiar with functional programming, but XSLT still feels more theoretical than practical. I really understand the benefit of not having to write yet another language parser, but programming in XML, by hand, feels really wrong. Maybe using a better tool would help.
What I Hate About SQL
The extensions I find most useful for a database generally aren't portable to other databases.
The approach that feels most natural is often really, really bad. I don't naturally think in terms of set operations.
Acquired Tastes
These are my preferences, based on the kind of work I've done and continue to do, the order in which I learned the languages, and just plain personal taste. In the spirit of generating new ideas, learning new techniques, and maybe understanding why things are done the way they're done, it's worth considering the different ways to do them.
The Pragmatic Programmers suggest learning a new language every year. This has already paid off for me. The more different languages I learn, the more I understand about programming in general. It's a lot easier to solve problems if you have a toolbox full of good tools.
I've learned several new techniques from branching out. For example, Ruby supports mixins with surprising ease. It's a natural way to solve certain problems where you might otherwise use a Decorator pattern. Seeing how Ruby uses mixins led me to ask how I might solve the same problem in Perl — and it's incredibly easy. It's not exactly a built-in language construct, but it's trivially easy to add, and now it's part of my standard Perl vocabulary.
Over time, my taste in languages has evolved. I used to worry about performance and variable-typing, but working with dynamic (or "agile") languages and learning to love test-driven development has biased me against strong-typing systems. Maybe learning a language like Haskell would balance my thoughts.
Every language is sacred in the eyes of its zealots, but there's bound to be someone out there for whom the language just doesn't feel right. In the open source world, we're fortunate to be able to pick and choose from several high-quality and free and open languages to find what fits our minds the best.
chromatic promotes free and open source software for O'Reilly's Open Technology Exchange.
![]() |
Essential Reading How to Keep Your Boss from Sinking Your Project Like it or not, your project needs management. Yet few good software projects can survive bad management. If you're a programmer on a high-visibility project, this PDF offers five principle guidelines for managing upward that will help you help your boss make the right decisions about setting project expectations, working with users and stakeholders, putting the project on the right track and keeping it there. The PDF also covers what problems cause projects to fail and how to fix them, and what you can do to keep your software project from running into trouble. Read Online--Safari Search this book on Safari: |
Return to ONLamp.com.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 251 through 266 of 266.
Previous Page
-
Java: Catch all exceptions?
2003-05-13 15:11:20 zipwow [Reply | View]
-
Java: Catch all exceptions?
2003-05-13 15:27:43 anonymous2 [Reply | View]
I believe if you change "catch" to "care about" you're closer to what the author intends, but you still have to add "non-runtime" in front of exceptions (The compiler doesn't force every method to throw or catch runtime exceptions thrown in child methods).
I circumvent that behavior by declaring (almost) all of my exceptions as runtime.
I don't think Java should require the addition of "throws" to the method declaration for non-runtime exceptions. It should just be implied.
Does anyone have an advantage that forcing the "throws" provides? -
Java: Catch all exceptions?
2003-05-13 17:50:15 anonymous2 [Reply | View]
You should use exception handling to indicate errors.
RuntimeExceptions indicate errors in your program logic that you should fix (things like NullPointerExceptions). RuntimeExceptions are things that should NEVER happen in properly debugged code.
Checked exceptions, things like FileNotFoundException are things that can reasonably happen in code AND you cannot defend against in your code (like you can a NullPointer). Checked exceptions do not indicate logic errors in your code.
If the compiler did not force you to deal with checked exceptions then you would have to know what each method throws. To do this you would presumably need to check the documentation.
If you are going to write "proper" robust code you are going to want to handle all errors (right?). WOuld you rather have the compiler help you out or would you rahter do things manually?
One way or antoerh you should be handling all the errors - why not let the compiler help? -
Java: Catch all exceptions?
2003-05-13 17:19:41 anonymous2 [Reply | View]
I think the language designers wanted you to make throwing the exception part of the interface of your own function. If someone calling your function doesn't know how you implemented it, how do they know they should check for a HashTableSober exception, or somesuch? The client code shouldn't crash because of something it wasn't informed of.
I think that's the idea, anyway. In practice, implied "throws" would make life more convenient.
Grant_Watson on Slashdot -
Java: Catch all exceptions?
2003-05-13 15:44:26 anonymous2 [Reply | View]
Sure, it is always about adding a sanity check. I believe that many if not all of the non-runtime checks are brain dead and cannot be ignored without severly breaking your program.
I may not get an interruption while waiting on a semaphore very often, but when it happens you better damn know that it did.
-
C Nitpick: exec* are not part of the language
2003-05-13 15:10:45 anonymous2 [Reply | View]
The exec* family of functions aren't part of the C standard library. They're part of the Unix system library (which is named differently on each platform...). Your beef in this case is with Unix, not the C language.
-
C Nitpick: exec* are not part of the language
2003-05-13 15:40:30 chromatic |
[Reply | View]
Good point, thank you. An earlier version of the article mentionedsprintf,snprintf, and friends, but one of the reviewers gave me a good mnemonic to remember them and pointed outexec*and friends, which are harder to remember.
I'm not sure I can make a clear mental separation between C and Unix, though. :) -
C Nitpick: exec* are not part of the language
2003-05-13 16:35:19 anonymous2 [Reply | View]
<blockquote>one of the reviewers gave me a good mnemonic</blockquote>
Care to share? -
C Nitpick: exec* are not part of the language
2003-05-13 19:08:29 anonymous2 [Reply | View]
I've never had any difficulty keeping printf, fprintf, sprintf, snprintf, vsprintf, vsnprintf, wsnprintf, etc. straight. For one thing, I almost never use sprintf() because of the possibility of buffer overflows. That aside, it's a pretty common convention in C to use 'n' in a function name to indicate a bounded buffer (strncpy comes to mind). I once saw a laughable use of strncat, though: this programmer evidently thought repeatedly strncat()'ing with the same buffer length would prevent buffer overflows.
What does trip me up from time to time is the difference between fputs() and puts(). I keep expecting the stream (FILE) to come first, although I'm getting better at it. I often have trouble remembering whether putchar() or putc() writes to stdout Sometimes I just wish they wouldn't have these convenient shortcuts, and just make you specify stdout explicitly.
-
Balancing your thoughts
2003-05-13 14:10:28 anonymous2 [Reply | View]
A language very similar to Haskell is Clean. It is definitely worth checking out especially if you value speed (compilation and execution)
http://www.cs.kun.nl/~clean/
-
Namespaces in PHP5
2003-05-13 04:23:52 wjgilmore [Reply | View]
Interesting article. Just an FYI that PHP namespaces will be made available in version 5. Hooray!
-wjg -
Namespaces in PHP5
2003-05-14 01:42:34 anonymous2 [Reply | View]
Who cares. PHP is for retards anyhow. Perl is way superior. -
Namespaces in PHP5
2003-05-13 15:28:51 anonymous2 [Reply | View]
That's just like mentioning features that are going to be in Perl6. It really doesn't matter as you don't have it right now!
-
Namespaces in PHP5
2003-05-13 17:03:50 anonymous2 [Reply | View]
Ya, So true.
I hear MySQL is going to have stored procedures, sub selects, and views too...








Isn't that the description of adding it to the throws clause of your own method?
-Zipwow