Cargo-cult typing, or: Objective-C's default type is id
blog.metaobject.com
blog.metaobject.com
It is true that we can omit id and still satisfy the compiler. This is the same way that we can leave out the int type in C function declarations, or completely leave out function declarations altogether if we're dealing only in ints. In both cases, though, nobody does this — and if you do this, your code will read as a mistake to anybody who has read any other Objective-C code from the past decade.
We include these "extraneous" types not for the compiler, but to make our intentions clear to the reader. This is the same reason many people and style guides use one-line compound statements or typedefs that make the token longer but more descriptive — at the same time we sacrifice a little brevity, we also sacrifice ambiguity.
It probably isn't always true that explicit is better than implicit, but it's a very good rule of thumb. If you have to choose one for a default mode, "explicit" is definitely the better choice.
As for the larger point about dynamic types, I and a lot of other old-timey Objective-C programmers felt the same way. It seems to me that powerfully dynamic languages like Smalltalk are good, and powerfully static languages like OCaml are good, but C's type system is just anemic and more bureaucratic than anything else. However, in practice, all these static facilities in Objective-C have proven really useful for writing real programs. The static analyzer can catch all sorts of errors. A combination of very consistent conventions and sophisticated static constraints has even allowed memory management to be handled for us completely through static analysis — that is just amazing, and the benefits of being rid of that responsibility are huge. Apple keeps moving in the static direction because those are the additions that Objective-C programmers are finding useful in practice.
I agree, the more loosey-goosey dynamic and implicit style works better for exploratory programming. I'll sometimes write spikes that way. But a book teaching how to write code in the language is not a throwaway spike solution, and in that environment you should either explicitly tell readers you're teaching them a style that nobody else uses in production code or — and this is my preference — just write idiomatic code.
Generally writing a book about a language, you should not be occupied with what a FUTURE version of a language noone has announced MIGHT be.
In what way? Is there a measurable performance penalty paid for including the return type of (id)?
There is clearly a penalty paid in understanding and clarity, which flies in the face of the topic of your book. If you have explained that, than your reviewers aren't reading your book (which is a different problem of sorts).
Considering your books is targeted at the Apple audience, I think these are fair questions. Simply saying "because I can" or "this is how I've always done it" isn't really good enough. However, if you can point to real benefits to not including the default type in the definition (something more subjective than clarity in your eyes), then you should spend time explaining that.
The KISS principle or Occam's razor strongly suggest that those who introduce redundant entities are the ones who have to justify their choice, not the other way around.
"Everybody does it this way, now" is a reason, though we can debate whether it is a good one.
Now that I know, though, it has become syntactic noise for me.
Also, the "call a method"/"send a message" distinction is useful. I don't usually criticize people for using the "method call" terminology, but I almost always use the latter since the idea of messages is central to a lot of Objective-C idioms (e.g. the responder chain in Cocoa). It's helpful to think in terms of messages.
Beyond that, I find that mentally framing your program as objects sending messages to each other and responding as they see fit just leads more naturally to good OO designs. It implies a lack of coupling that is absent from the semantics of a "method call."
In the pattern I'm talking about, you don't necessarily send the message to the object that will actually respond to the message. You send it to another object and the objects in the chain talk to each other until one agrees to handle it or the end of the chain is reached. This pattern can be used with classes that might not even implement the method. You can do something similar with C# or Java using reflection, but it is more natural in Objective-C.
This seems to be greatly overstating the complexity of what happens.
There are two cases I can think of that this might describe. One is event messages. This is handled by having everything that can handle an event subclass from NSResponder. NSResponder is then full of little stub methods like this:
- (void)mouseDown: (NSEvent *)event {
[[self nextResponder] mouseDown: event];
}
You then subclass NSResponder and override the methods you're interested in. This would look pretty much the same in any OO language with subclassing and inheritance, which is just about all of them.(NSResponder actually gets a bit clever here, and funnels these methods through a function call that handles general look-up-and-send logic, but the basic principle of what's going on is what you see above.)
The other case is action methods with a nil target. Here, there's just a method that looks like this:
- (void)sendAction: (SEL)action fromSender: (id)sender {
for(id target = [self firstResponder]; target != nil; target = [target nextResponder]) {
if([target respondsToSelector: action]) {
[target performSelector: action withObject: sender];
break;
}
}
}
Again, the real implementation is a bit more clever, but that's the essential part.The use of reflection rules out C++, but it would look pretty much the same in Java. The inside of the loop in Java would look something like:
try {
Method m = target.getClass().getMethod(action, new Class[]{ Object.class })
m.invoke(target, sender);
break;
} catch(NoSuchMethodException e) {}
The fact that action would have to be a String is a bit smelly, but overall it's pretty much the same code as the Objective-C version. The fact that this pattern isn't used in Java comes down to tradition and happenstance, not any sort of worse affinity on the part of the language.Yes they are similar, and most often sending a message of a particular name will invoke a method of that name via the messenger. But they are different in ways that matter, so it is better to use the correct terminology.
However, it's not really a big deal, you will typically be understood either way. However, I don't quite understand why using the correct terminology is so difficult. "Send a message" vs. "Call a method". Same number of words, same number of syllables. OK, 1 letter more to type...
A method is an implementation, something that executes. Protocols do not contain implementations, though there have been proposals to actually add methods to protocols. Talking about such a proposal to add methods to protocols becomes difficult when you have called the messages that are already there methods...
How would it definitely indicate that it returns an object? It has the same signature whether it's an accessor or a behavior, so you don't know if the implementation is returning an object.
That turns out not to be the case:
-(void)behavior;
-accessor;- (id)foo
reduces readability, while
-(id) foo
is much better.
I realize the first one is more common in Apple and is probably more standard, but it begs the question why such a horrible convention ever caught on.
-foo
is even better. :-)
-(void)foo;
This is how C and by extension, Objective-C work: to indicate "nothing" you have to put in something, specifically "void".I also think his larger point is important though. The general trend towards more explicit typing being the default is a move away from Objective-C's Smalltalk roots and this is a bad trend.
I suspect it is somewhat influenced by Java programmers invading the Objective-C space and spreading their rigid typing, pattern-itis cancer.
He also once said to me that Objective-C being a statically typesafe language was a fact that could not be argued about.
Hmm.
Also agreed that whether the (id) is there or not doesn't matter (much). I myself have been happily reading over it without really noticing for upwards of two decades now. What bugs me is the claim that it has to be there.
If you think it increases readability for you, OK. If you want to conform to whatever Apple recommends, OK.
What worries me is that apparently (the discussion confirms this), Apple has managed to push the notion that there always have to be types, that Objective-C is not an optionally statically typed language.
Both parts of "optionally statically typed" are useful, the static typing and the fact that it is optional.
The problem is the stricter enforcement, which I explain near the end "A dynamic language": explorative programming, where you are still figuring out what exactly to do, has become much more onerous, widening the gap between Objective-C and other dynamic languages such as Ruby or Python.
Previously, the compiler would warn you that you hadn't gotten the types yet, but as long as you were in the id subset (an extended subset, actually) that didn't matter and you could continue exploring until you got it right...and THEN write down all the types in order to have the positive effects of machine-checkable documentation.
With the changes, this is no longer possible, so you have to alway juggle (a) the site of the client (b) the implementation of the method and (c) the header with the method. That gets old fast.
I wish Apple would add local type inference to Objective-C, perhaps using the existing id type or some keyword like var in C#. It would make the code so much more quicker to write, less verbose, a bit easier to change.
I mean reading and writing something like:
var color = [UIColor green];
var viewController = [[MyViewController alloc] initWithBackgroundColor:color];
Is so much more pleasant as: UIColor *color = [UIColor green];
MyViewController *viewController = [[MyViewController alloc] initWithBackgroundColor:color]; id color = [UIColor green];
id viewController = [[MyViewController alloc] initWithBackgroundColor:color];
The UIColor* is largely there for your benefit, for the compiler it makes hardly a difference (pre ARC). If you don't find it a benefit, just leave it out.- (void):(int)arg :(int)anotherArg :(int)someArg { NSLog(@"%d %d %d", arg, anotherArg, someArg); }
[self:1:2:3];
Try it. It compiles. It runs. How great is that? In fact, even Apple has done this!
+ (id)functionWithControlPoints:(float)c1x :(float)c1y :(float)c2x :(float)c2y;
So does this make this the best option? Should we even name our methods?
Of course, that is silly. Naming our methods appropriately tells us a lot. It means we don't have to check the documentation as much, which means we can effectively write faster. We're communicating with our code and our teammates more efficiently. The same holds true for explicitly typing method return values.
So ask yourself this: what's the default storage mechanism for a property, assign or strong? Now what was it when ARC first was developed? That's right, it was different. So you could see how someone might be confused reading your code if you weren't explicit. They might spend time looking in the wrong places for a bug. So why not be explicit? It's not about you, it's about everyone else who is left with your code.
So Brad Cox doesn't explicitly type his returns. Neither did Nextstep sometimes. That was 20+ years ago. We didn't have fancy IDEs with autocomplete; keystrokes were a luxury. That's probably why they couldn't put the 'e' on 'creat' in libc.
Last couple of Objective-C articles making it to the top of Hacker News have been bad. Just a kind warning to any budding Objective-C developers: just because it made it onto Hacker News doesn't make it good.
@interface NSArray (XXHelpers)
-(instancetype)reverse;
@end
or even better @protocol XXReversing <NSObject>
@required
-(instancetype)reverse;
@end
@interface NSArray (XXHelpers)
<XXReversing>
@end
@interface NSOrderedSet (XXHelpers)
<XXReversing>
@endWith that said, I think Apple are pushing the language in that direction, because recently everyone started developing for iOS, even people who didn't program at all. Apple is trying to make it easy for them. I see nothing wrong with that. It may not be the best from a programmer viewpoint, but I can understand it.
P.S. I think this is why Microsoft has embraced static typing throughout its developer ecosystem. It makes learning from scratch much easier and developers (although not very experienced ones) flock to your platform. Of course this same thinking created ASP.NET and shortened the lives of many good men. But you cannot judge them, really.
The only reason i see why you wouldn't want the "compiler" to understand what data structure your function is accepting or returning is when this data structure is far too complex to be represented as a single type in a meaningful way (such as json deserialization returning "Array Or Dictionary" kinds of types). But apart from that, i can only see advantages in terms of IDE assist (autocompletion, refactoring), code clarity and safety.
That actually sounds really helpful and I'm not sure why people don't embrace it.
(One point you do make that is quite correct is that static typing is helpful for novices. And yet so many languages designed for beginners' use try to make things "easier" by turning typos into new variables, letting variables switch type, and generally poking their user in the eye. Never figured this one out - though I suppose this is good training for the novice programmer, because programming is such a straightforward activity and without having the language itself to keep them on their toes they might find it insufficiently exacting, not demanding enough, and requiring basically nothing in the form of concentration, thought and precision.)
Visual Basic, Access, Excel and ASP were all dynamically typed, and were very responsible for exploding Windows' market share from Windows 3.0 onwards as it encouraged many to start programming who never would have considered it before.
Unfortunately they created an ecosystem of novice developers that many within MS I think despised and looked down on and which gave their platform a bit of an unprofessional reputation.
Then along came Java and showed them that a relatively sophisticated and strongly typed language could raise the entry bar just a bit, but result in generally much higher quality of end product, so along came C# and out went VB and COM.
'Classic' ASP has also been responsible for much hair loss in early webmins.
It is having both that is the beauty of it, in more ways than one. Very expressive high level code and low-level bit banging when you need it. Dynamic, explorative programming that can be hardened to stricter typing regimes once a design stabilizes.
What I am complaining about is not just that this duality seems to be getting lost, but that it is lost by apparently combining the worst of both worlds, the expressiveness and rigidity of statically typed language and the performance of a dynamic language. And what's worse, it appears that it's being done without even a conscious decision, but more by drifting aimlessly, picking up features and practices more or less haphazardly.