Methods are actions, not objects, so the most concise description for a method is usually a verb. Describing it as a noun instead requires adding prepositions and turning verbs to the gerund '-ing' form.
I realized this because I write both Ruby and Objective-C, and sometimes write basically the same thing in idiomatic forms of both languages. In idiomatic Ruby method-chaining, your code is a series of verbs with the relations between the actions defined by the '.' or the '()' signs, rather than by words such as 'to', 'from', 'with', or 'by' present in the name of the method.
What really creates the impression that Objective-C speaks in terms of nouns is that Cocoa tends to promote immutable objects more than Ruby does (e.g. the only way to get an immutable string or array in Ruby is to freeze a mutable one, while you'll almost never get a mutable array in Cocoa unless you create one yourself), so you probably do spend more time asking your objects for other objects than you do in Ruby.
Although the verbosity can get overwhelming, I actually like this about Objective-C. In terser dynamic languages, I'm constantly having to confirm (either mentally or in the docs) which methods mutate and which return a new object. Cocoa's naming conventions mean I pretty much never have to do that.
pop
push
shift
unshift
<<
clear
replace
delete (and friends)
keep_if
As an even more extreme example, IO contains precisely one bang-method, despite the fact that probably 75% of IO's instance methods are destructive.The general rule seems to be that if there's a mutating and non-mutating version of the same method, the mutating one will get a bang, but when there's a mutating method with no counterpart, it might get a bang but probably won't.
- There is a safer alternative (e.g. mutating vs. non-mutating or skipped validation)
- It should only be called once in a process (e.g. Padrino.start!)
- It is non-reversible (many statemachine libraries use action! as the way to invoke state transitions, which might not be reversible)
This doesn't mean that every method needs to be suffixed by ! if it does something destructive. `delete` in the context of an ORM is standard, so it doesn't have a bang. The whole point of `pop` is to manipulate the receiver: no point in warning about it. IO is always destructive, so ! doesn't make sense either.Yegge basically completely agrees with you. Once I started thinking of OOP in these terms it helped me, as a programmer, a lot.
I wonder if we can relate your observation to the fact that "messages" (nouns) are preferred to "methods" (verbs) in Obj-C.
Verbose selectors make Objective-C self-documenting at the cost of extra typing. But autocomplete solves this problem because you only ever have to type 2-5 characters to insert the method you want (once you are experienced enough to predict what autocomplete will spit out).
Objective-C naming conventions form predictable patterns. Inexperienced programmers gripe because they have yet to figure out these patterns, good programmers love Objective-C because they understand these patterns and can therefore predict the name of a method and its arguments and great programmers write their own classes that use these patterns.
With it's C-based syntax, Objective-C isn't as clean as Python or Ruby, but due to the explicit naming conventions, I think it's more readable than Java or JavaScript.
if ([[data objectForKey:@"released"] isKindOfClass:[NSNull class]]) {
if ([[data objectForKey:@"posterUrl"] isKindOfClass:[NSString class]]) {
They weren't right next to each other, and at a glance, I misread to assume they were doing the same thing. There's too much shit in the way of the actual differences (NSNull vs. NSString in this case) that I have a bad tendency to gloss over the details. Coming from ruby, the closest syntactical equivalent: if (data['released'].class == NilClass) {
if (data['posterUrl'].class == String) {
Is so much clearer to me when glancing through code. That doesn't even touch on how you'd actually write that sort of thing (data['released'].nil?) which is infinitely more concise than either example. I know this is a bit of a contrived example, and I certainly could find better ones. I just find the 120 character long method evocations to consistently blur the details for me, and this just happens to be the freshest instance.To be fair, I've only been doing iOS stuff for about a month. Does this trend reverse after you've been writing obj-c for a while?
For instance, I look at the first example you posted and I know exactly what it does. It's very natural for me to read.
FWIW, extracting nested expressions into local variables helps a lot with nested method calls.
connection:didReceiveResponse: addObserverForName:object:queue:usingBlock: drawAtPoint:forWidth:withFont:minFontSize:actualFontSize:lineBreakMode:baselineAdjustment:
Personally, once you have more than two humps in your camel case, my eyes have trouble scanning.
HWND hwnd = CreateWindow("MainWClass", "Sample",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
(HWND) NULL, (HMENU) NULL,
hinstance, (LPVOID) NULL);
And here's a long method call in Objective-C: NSString *filtered = [unfiltered stringByReplacingOccurrencesOfString:@"verbose"
withString:@"explicit"
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, [unfiltered length])];
Admittedly not equivalent examples, but I couldn't quickly find a Cocoa method call with eleven parameters. My argument is that if you're not intimately familiar with these two calls, CreateWindow() is pretty cryptic. You only get the most general sense of what it's doing without consulting a reference. Objective-C's strange method naming scheme (taken from Smalltalk) makes complex method calls much easier to understand in situ.Allow me to introduce you to NSBitmapImageRep, which holds the dubious honor of being initialized by the longest public selector in all of Cocoa. Here's a contrived example:
NSBitmapImageRep *rep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:data
pixelsWide:640 pixelsHigh:480
bitsPerSample:8 samplesPerPixel:3
hasAlpha:NO isPlanar:YES
colorSpaceName:@"NSCalibratedRGBColorSpace" bitmapFormat:NULL
bytesPerRow:0 bitsPerPixel:0];
edit: formatting NSString *filtered = [unfiltered replace:@"verbose"
with:@"explicit"
options:CaseInsensitive
range:Range(0, [unfiltered length])];
Of course, I'm making assumptions about what those parameters mean. But I find this much more clear, assuming the intentions are what I think they are.On the second line, you didn't mention at all what is passed in.
For the last two, removing the NS prefix would work if Objective-C had some kind of namespacing support. Currently it doesn't, so the NS prefix is kinda needed.
I'm not sure what you mean by not mentioning what is passed in. Keep in mind: I have never programmed in Objective-C. I am going on intuition alone.
No namespace support is a bummer - it means you're going to end up with long identifiers all over the place.