Hacker News new | comments | show | ask | jobs | submit login
Cargo-cult typing, or: Objective-C's default type is id (metaobject.com)
66 points by mpweiher 1117 days ago | hide | past | web | 59 comments | favorite



This article is technically correct, which I'm told is the best kind of correct. However, it's also missing the forest for the trees.

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.


I agree totally. I must say, that I have seen typeless declarations in code, but I do find them to be a nuisance. Adding (id) to a declaration isn't really that hard, and makes the code a lot more radable. Why raise the treshold for the ability to read Objective-C?


Agreed. It's generally a good idea to write readable code. There's a reason why explicitness and redundancy are common in natural language.


It seems though that if you're writing a book that is used as a documentation reference, you should be fitting with the coding standards that Apple uses for their own documentation. You would be hard pressed to find any framework header or apple doc that doesn't use -(id)mymethod


Why? (Hint: the book demonstrates that quite a few things that Apple recommends are, how shall I say it, not entirely optimal)


> how shall I say it, not entirely optimal

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.


There is no execution side difference for writing or omitting the (id). The point is that you shouldn't follow advice blindly, even if it comes from Apple.

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.


Because Apple is constantly evolving the language, and you could genuinely find that they change the compiler to deprecate this "feature" down the road.


Or they could enforce this feature.

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.


Or you could write idiomatic Objective-C and maybe save a chapter or section for being clever.


Well that was a tease. :-) Any examples?


Hi Ken! In the book? ;-)


I've been developing in Objective-C for years now and I didn't know you could declare a method without explicitly stating a return type. I imagine many other people don't know that either, so I'd argue that saying -(id)doSomething; is more readable.

Now that I know, though, it has become syntactic noise for me.


- (id)foo tells me that the method returns an object rather than simply performs an action. The article doesn't explain why this makes Objective-C harder or less fun. It reminds me of the anal-retentive complaints from long-time NeXTStep developers that I used to see on the Objective-C mailing list before I unsubscribed. Those guys don't even want you saying "call a method" instead of "send a message".


-foo would just as definitively indicate that it returns an object. The return type defaults to id, not void.

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.


How is the call/send distinction useful, exactly? You call a method by sending a message. When you send a message, it calls a method. They're two aspects of the same thing, and using one rather than the other never gives you any additional information.


Forwarding and dynamic resolution make the relationship between a message send and a method call less than completely straightforward in Objective-C. You do ultimately end up calling some method, but it's not necessarily the method you think you're going to call. I mean, remember: Roughly every Cocoa program in existence routinely calls an unknown method of an unknown object by sending a known message to a known object and having the objects work it out amongst themselves (like I mentioned earlier, the responder chain).

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."


How is that any different from calling a method on a known interface supported by an unknown object in a language such as eg. C# or Java?


If you're going through an interface, it sounds like you have to have a specific object in mind and you just don't know its class.

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.


> the objects in the chain talk to each other until one agrees to handle it

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.


Sending a message does not necessarily call a method (especially not the one indicated by the name, messages to nil do nothing, ...), and you can call methods without going through the messenger (get the IMP, call it).

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...


There's nothing difficult about it. It's just a pedantic and pointless distinction most of the time. To be more specific about what I was referring to, some folks on the Objective-C mailing list didn't even want methods in a protocol to be called methods. Instead they are to be called "method signatures". I roll my eyes.


Well, maybe that's because they aren't actually methods?

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...


> -foo would just as definitively indicate that it returns an object. The return type defaults to id, not void.

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.


> It has the same signature whether it's an accessor or a behavior,

That turns out not to be the case:

    -(void)behavior;

    -accessor;


What does drive me nuts is the non-sensical spacing.

- (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.


Apple is wildly inconsistent with their example code and even the templates within XCode when you add a ViewController for example. Same line for the first accolade or the start or the next time in the same file. I always find myself correcting their code, that's just bad.


Call me crazy but I actually like the first way better. It's easier for me to read, personally. Different strokes though.


However,

-foo

is even better. :-)


No, -foo is confusing and reduces readability.


How so?


It is difficult to tell at a glance if -foo is returning something or just modifying state.


No it's not. If it didn't return anything, it would be

    -(void)foo;
This is how C and by extension, Objective-C work: to indicate "nothing" you have to put in something, specifically "void".


This is equivalent to the semicolons in javascript debate. Pointless bike-shedding. You save 4 characters, big deal.


I agree. Putting in the id return or not is irrelevant.

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.


You are right about your suspicion. In fact, one of the central characters came out an flatly stated that Java was a better language. He had a hand in the "modern syntax" (anyone remember that?), property dot notation and the GC implementation that ultimately failed.

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.


Thanks for the feedback. I thought I was fairly clear in the article that I don't really care whether you add the (id) or not, as long as you know why you are doing it, but it appears I wasn't. I've amended the article to be better about this.

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.


You haven't actually explained why you think static typing is such a problem... `Id` is still there. Use it when you want. But if I want to call "setText:" on a UILabel and documentation says it needs an NSString... That's pretty handy.


The reason I haven't explained why static typing is such a problem is because I don't think it is, for example I agree with what you write.

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.


Somewhat unrelated ...

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];


You could just write:

   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.


Problem with using id is that it has no knowledge of type information. Xcode wont be able to autocomplete. This in contrast with using the var keyword in C#.


As I wrote: it's there for your benefit...


I'd argue that typing (id) explicitly is actually good, because it gets the programmer and any readers of the code in the habit of seeing that there is a space to notate return types. Lowering the cognitive barrier to static typing can only be a good thing.


The semicolon debate is even more substantive because that potentially leads to real bugs of the difficult-to-debug variety.


You can also define methods without labels:

- (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.


You could always use instancetype to indicate that you're getting an instance of the class back.

  @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>
  @end


Static typing is easy for newbie developers. It sets hard boundaries, and when you remember those rules, it's easy to follow what the code does. You don't have to step through it in your head and think about it much to figure it out.

With 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.


Easily comprehensible code? Hard boundaries, and the ability to figure out what the code does without having to mentally step through it? Perhaps you can even click a button when the cursor is on an identifier, and be taken to the definition of the entity of that name, or the small subset of possible entities of that name that this identifier could refer to? Possibly the computer could check your work without your even having to run it, and actually prove that certain types of error cannot possible occur?

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.)


Actually Microsoft's earlier and very successful development tools were all dynamically typed versions of Basic.

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.


Sorry for nitpicking, but COM is in no way out - it forms the basis for WinRT. Also COM in itself doesn't have much to do with dynamic typing - sure there is IDispatch which allows your code to be called from dynamic languages, but that's about it.


Usually dynamic languages are recommended for newbies - such as Python. Newbies don't care about basic correctness or safety of their programs, it's more important to "get something on the screen" quickly to stay motivated.


I really don't see how static typing can be associated to "newbie" coding in any way. Haskell, Golang, Ocaml, and even Scala, clearly aren't aimed at newbies "let's code an app cause that's cool" kind of developers, and they both embrace static typing.

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.


I think what slavoingilizov is that it makes it easier for companies to make use of inexperienced programmers, not that it helps non-programmers get up and running quickly. The additional automatic checks and IDE assistance enabled by static analysis can help partially paper over a novice's shortcomings.


The more vociferously a language-feature is debated on Hacker News, the less it matters.


Sounds like this guy wishes Objective-C was Smalltalk with C-syntax.


Well, it pretty much is.


No, it's not. That's what this guy is complaining about. Objective-C is dynamically typed, but allows for static typing; Smalltalk is always dynamically typed.


It's not Smalltalk with C syntax. It's C with C Syntax and an object system with Smalltalk syntax smashed together.

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.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | DMCA | Apply to YC | Contact

Search: