Objective C, readability, and language design
For the last several months, I’ve been teaching myself Objective C and Cocoa. On the plus side, the framework is well designed. It’s the Objective C syntax that still has me tearing my hair out.
Square brackets
The biggest difference between Objective C and C++/Java/C#/ActionScript is the syntax for sending messages to objects. Instead of using dots or arrows, you use square brackets:
ActionScript / JavaScript
foo=object.method()
Objective C
foo=[object method]
Looking at it like this, the syntax doesn’t look half bad. The problem comes when you start chaining these methods together.
Let’s say you want to get the screen bounds of the window that holds a view object:
NSRect screenRect = [[[myView window] screen] frame]
Reading this syntax isn’t bad, but writing it is a pain. Every time you want to add a new method call at the end, you have to go all the way to the beginning of the line to add an open square bracket.
Long names
To compound matters, most of the method names are much longer than above:
sortedDocuments = [[[documents allKeys] filteredArrayUsingPredicate:predicate] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
By shortening some names (e.g., filteredArrayUsingPredicate -> filter) and using dots and parentheses instead of square brackets, you might end up with something like this:
sortedDocuments = documents.keys.filter(predicate).sort(caseInsensitive);
Strings and collections
Some languages, such as C# and ActionScript define string classes as an integral part of the language. This allows those languages to add common operations like string concatenation directly into the language. Others, like C++, do not define strings directly in the language, but allow class designers to override operators to, in effect, extend the syntax of the language.
In Objective C, string objects are only barely integrated into the language. The only concession to usability is that there is a syntax to define string literals (@”xxx”).
In order to concatenate strings, you end up doing something like this:
ActionScript / JavaScript
"The file " + filename + " could not be found."
Objective C
[[@"The file " stringByAppendingString:filename] stringByAppendingString: @" could not be found."]
The same is true of collection classes, such as arrays and hash tables.
Objective C
[wordMap setObject:[words objectAtIndex:i] forKey:key]
ActionScript / JavaScript
wordMap[key] = words[i]
Frankenstein – Not 100% object-oriented
Like all of the C-based languages, there are weird cracks in the language where the object-oriented and non-OO worlds meet. In Objective C, you can define structs and objects, which work totally differently. You can define both functions (denoted by parentheses) and methods (denoted by square brackets). To confuse matters further, Objective C 2.0 defines getters and setters, which are accessed using dot notation, even though the implementation is done through methods, which normally don’t use dots.
One of the classic ways in which the OO and non-OO worlds meet is in dealing with primitive numeric types. Should an int be represented in the most efficient way possible (e.g., 4 bytes of raw data), or should it be a full-fledged object, so it can be used in places that expect objects (e.g., collection classes).
Many languages provide both primitive and object versions of things like integers, and Objective C is no exception. Modern languages like Java 5 and C# simplify this through automatic boxing/unboxing, which means that these languages automatically convert between primitive types (e.g., int) and the object types (e.g., Integer).
Here is a line of actual code that I wrote the other day:
Objective C
[dict setObject:[NSNumber numberWithInt:prio++] forKey:[NSNumber numberWithInt:matcher.type]]
Now you might look at that line of code and say that it is too complicated to put on a single line. Well, if the language (a) had built-in hashes, and (b) did auto-boxing/unboxing, the above line would look like this:
Hypothetical Objective C with hashes and boxing/unboxing
dict[matcher.type] = prio++
Syntax is important
From a mathematical point of view, the specific syntactic choices are not all that important in defining what a language can do. But from a human point of view, the purpose of a language is to make it easy to express and communicate ideas. Computer programs are hard enough to read and write as it is. To the extent that a syntax makes it hard to express ideas clearly, it makes the task of programming harder than it needs to be.
For those of you who like Objective C, I know… I know… every language has its warts. I spent a good chunk of my life working in C++, and I know that a badly written C++ program is as hard to read as the worst Perl program.
To be fair to the other side, maybe the issue is that I know the warts of C++, whereas I am only now learning new warts in Objective C. So maybe I’m just overreacting when I tear my hair out and throw things across the room.
Yeah, right.