Interpretation of static types in Cocoa [Was: Immutable discomfort]
Interpretation of static types in Cocoa [Was: Immutable discomfort]
- Subject: Interpretation of static types in Cocoa [Was: Immutable discomfort]
- From: Philippe Mougin <email@hidden>
- Date: Fri, 19 Dec 2003 16:56:40 +0100
>> If you define your API to accept an immutable object (aka NSArray)
>> then folks should either give you an immutable object or mutable
>> one but abstain from changing anything in it.
>
> Actually, doesn't taking an NSArray as an argument imply that the
> the method won't change it (not that the caller isn't free to
> pass in an NSMutableArray?)
Well, I've seen some people using or advocating such conventions,
including some skilled Cocoa engineers at Apple.
For instance, I've seen it expressed, quite clearly, like that:
----- quote ----
"In Cocoa, the types declared for arguments and return values in the
API are more about what the caller can do with the return value or what
the method will do with the argument rather than what the return value
really will be or what the argument must be."
------------------
However, in the spirit of good technical debate, I'd like to state it
that, IMHO, these conventions are bad and should not be used. They
leads to numerous problems, including the fact that, AFAIK, they go
against decades of research and refinement in the definition and
interpretation of type systems by the whole object-oriented programming
community.
In the spirit of "programming by contract", which provides us with a
reasonably good and well known model for the interpretation of types in
Objective-C, the type of an argument should be interpreted as a
precondition (i.e., a requirement that the caller must respect). It
should NOT be interpreted as a promise made by the method that it will
consider the static type to be the *exact* type of the argument. Thus,
if a method declares, using static typing in its signature, that it
takes an argument typed as NSArray, this should not be interpreted as a
promise that the method will not modify the NSArray. Form the point of
view of the caller, it should be interpreted as a requirement for it,
in order to fulfill its "contract", to pass an instance of NSArray or
of a subclass of NSArray. No more, no less.
As you know, NSArray, *considered as the specification of an abstract
data type*, do not imply immutability (the best proof for that is the
existence of a mutable subclass, NSMutableArray). At run-time, it is
perfectly acceptable (as far as the type system is concerned) for the
invoked method to determine that the actual argument is in fact an
instance of NSMutableArray and to modify it.
Likewise, the statically declared return type of a method should be
interpreted as a promise the method makes to its caller regarding the
actual class (or superclass) of the returned object. It should NOT be
interpreted as a requirement the method impose on its caller about what
the latter should consider to be the *exact* type of the returned
object, and thus should/should not do with the returned object.
The correct way for asking/enforcing some code (a method we call or a
caller we give back an object) to follow some behavior regarding a
specific object is not to defines weird conventions, that are
incompatible with the otherwise generally accepted interpretation of
the type system, and are impossible to generalize (at least, if these
convention could be generalized to the point of providing a complete
and consistent alternative interpretation of the type system, then the
terms of the debate could be different, but it is not the case). IMO,
The correct way is to provide it, *at run-time*, an object of a class
that, as an abstract data type, defines (and possibly enforces) this
behavior. Alternatively, or in complement, this can be specified by
some specific documentation for the methods (like, for example, a
sentence that states "the returned object must not be modified"). But
doing it by corrupting the interpretation of the type system or
introducing exceptions to this interpretation is not a good idea.
For a dynamically typed language like Objective-C this point should be
even more obvious. What we can/should do with an object is primarily
defined by the actual class (and state) of the object at run-time, not
by the fact it was returned/passed, at some point in its lifetime,
by/to a method that declare in its header to use such or such static
type. Otherwise, we would have a hard time doing very useful things
with objects coming from a method that declare returning an NSObject *,
for example. Trying to interpret the static type of the arguments and
return value as the *exact* type of the object which defines what some
code should/should not do with the object is generally a dead-end in
the context of a general purpose object-oriented language, because it
rules-out downcasting. Trying to do that in the context of general
purpose and primarily dynamically typed object language like
Objective-C is even more strange.
I'd like to strongly advise anyone using Cocoa to stick to the well
established approach described here when designing Objective-C classes.
An approach that we find pervasively documented and used in other
object-oriented languages and frameworks throughout the industry. Using
conventions incompatible with it equals to giving up any hope in a
reasonably sound, general and intuitive interpretation of our type
system. This leads to unintuitive idioms, complexity, unintended
limitations, inconsistent interpretation of the type system, and
brittle code (and, if this is not enough, this might also make your car
consume more oil ;-)
Best,
Philippe Mougin
_______________________________________________
cocoa-dev mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/cocoa-dev
Do not post admin requests to the list. They will be ignored.