robrix / RXCollections

Obsolete—use https://github.com/robrix/Reducers instead.
MIT License
475 stars 25 forks source link

NSFastEnumeration at the NSObject level? #3

Closed groue closed 11 years ago

groue commented 11 years ago

This "issue" is more a request for comments than an real issue. I personally do not have any problem with RXCollections.

The README states:

All Objective-C objects descending from NSObject now conform to NSFastEnumeration, simply returning themselves if they do not otherwise implement it.

That's pretty interesting.

I'm the author of GRMustache, a template engine that needs to distinguish "regular" objects from "enumerable" objects. I happen to use the conformance to the NSFastEnumeration protocol as a criteria.

So you gave me a sweat :-) Actually it looks like you do not add the protocol to NSObject, but rather just implement countByEnumeratingWithState:objects:count:. So it's okay, users that embed both our libraries won't have funny bugs.

However, imagine the following scenario: in a desire for GRMustache speed, I micro-benchmark conformsToProtocol: and respondsToSelector: RespondsToSelector: wins. Wondering whether I should stop testing for the protocol conformance and switch to the selector testing, I'll have to evaluate the chances that my change triggers a surprising behavior, that is to say, a "bug".

I may think "How an object responding to countByEnumeratingWithState:objects:count: could not be an enumerable in the head of the developer ? OK, let's test how the compiler behaves when I write a loop with an object that does not declare NSFastEnumeration conformance... Oh oh, it's totally OK as long as the object implements countBy... So chances are that the developer just did not bother declare the protocol conformance." And I'll make the change, with a clear conscience, since the compiler itself only tests for the selector, not the protocol.

And now we'd have a nice funny bug as soon as RXCollections and GRMustache and used together :-)

What do you think?

robrix commented 11 years ago

Thank you for the thoughtful discussion of this! Above all, it’s a reminder that I never should have committed that in the first place. I’m removing that category, and will not be re-adding it.

As I recall, -conformsToProtocol: needs to take a global lock of the runtime, whereas -respondsToSelector: does not.

That said, if this sort of problem comes up again elsewhere, you might want to consider adding a method which objects can implement (with default implementations for collection classes, perhaps) to declare whether or not they should be treated as “regular” or “enumerable.” If it’s an instance method, you can also use this to let individual objects opt in or out, although whether that is useful or not is anybody’s guess.

Again, thanks for sharing your thoughts, and thanks for making me aware of GRMustache! I always thought it was a cute templating system, and I’m glad to see what looks like quite a mature implementation under active development.

groue commented 11 years ago

That said, if this sort of problem comes up again elsewhere, you might want to consider adding a method which objects can implement (with default implementations for collection classes, perhaps) to declare whether or not they should be treated as “regular” or “enumerable.” If it’s an instance method, you can also use this to let individual objects opt in or out, although whether that is useful or not is anybody’s guess.

Yes, this was in my trend on thought. Not only a way to future-proof the library, but also a way to avoid dependency on the very nature of objects we easily take for granted as soon as they are defined by Foundation. I don't know about you, but I've done some Ruby programming as well: I understand pretty much why you experiment with the language and the runtime. Actually, it's so soft it's almost a call for action, isn't it :-) ? Should a user come and complain in case of a conflict, I'd actually have much difficulties deciding on where the blame should go. A first epidermic reaction would be to say "Your convenience library fucks the runtime - ask them to fix it". However, having NSArray, NSSet, etc. "opt in" the GRMustache "enumerable" would clear up things for good, while letting external runtime experiments live and prospers.

For now on, I won't do anything. Some say: if it ain't broken, don't fix it. I'm in a wait and see position :-)

Thanks for the conformsToProtocol/respondsToSelector comparison about the global lock. I was totally unaware of it. You shed light on the funny stacktrace of conformsToProtocol: in Instruments.

robrix commented 11 years ago

That said, if this sort of problem comes up again elsewhere, you might want to consider adding a method which objects can implement (with default implementations for collection classes, perhaps) to declare whether or not they should be treated as “regular” or “enumerable.” If it’s an instance method, you can also use this to let individual objects opt in or out, although whether that is useful or not is anybody’s guess.

Yes, this was in my trend on thought. Not only a way to future-proof the library, but also a way to avoid dependency on the very nature of objects we easily take for granted as soon as they are defined by Foundation. I don't know about you, but I've done some Ruby programming as well: I understand pretty much why you experiment with the language and the runtime. Actually, it's so soft it's almost a call for action, isn't it :-) ?

Sometimes the temptation does indeed become too great to bear :) Should a user come and complain in case of a conflict, I'd actually have much difficulties deciding on where the blame should go. A first epidermic reaction would be to say "Your convenience library fucks the runtime - ask them to fix it". However, having NSArray, NSSet, etc. "opt in" the GRMustache "enumerable" would clear up things for good, while letting external runtime experiments live and prospers.

I think my particular favourite bit about this approach is that it makes the interface by which the decisions are made explicit. As the Gang of Four Design Patterns book would put it, “encapsulate the concept that varies”—and in this case, the varying concept (the decision of whether to treat an object as a terminal or nonterminal) can easily be encapsulated by a method on the object, or by a delegate callback, or by a predicate object, or…

There are as many approaches as there are objects to message, really. Which one is “correct” is so often partly taste, and partly expected use cases.

For now on, I won't do anything. Some say: if it ain't broken, don't fix it. I'm in a wait and see position :-)

This sounds eminently reasonable to me. It is in my nature to dive deep into questions like this and come up with solutions for nonexistent problems, so I applaud your restraint ^_^ Thanks for the conformsToProtocol/respondsToSelector comparison about the global lock. I was totally unaware of it. You shed light on the funny stacktrace of conformsToProtocol: in Instruments.

If you have the opportunity to, I recommend reading the source for NSObject in Apple’s open-source runtime code: http://www.opensource.apple.com/source/objc4/objc4-532.2/runtime/NSObject.mm

Educational, certainly, but also entertaining :)

groue commented 11 years ago

This sounds eminently reasonable to me. It is in my nature to dive deep into questions like this and come up with solutions for nonexistent problems, so I applaud your restraint ^_^

I was quite close to use http://developer.apple.com/library/mac/#qa/qa1361/_index.html in order to check if GRMustache was used under debugger, and trigger some swizzling in order to avoid the debugger to stop on the many NSUndefinedKeyException that template rendering may raise (and catch). I have been able to restrain myself, and keep the swizzling as an opt-in feature :-) But it's hard, I 100% agree :-)

Thanks for your nice answers, Rob. I'll check your work, it's pretty interesting !