Martijn Faassen wrote: > Chris McDonough wrote: >> Personally I think that it's a fantasy to believe that the difference >> between >> an object created via a factory on-demand and an object simply returned >> should >> *never* matter to a caller. You may not want the caller to need to care, >> and >> it may be inconvenient to take into account circumstances where the caller >> needs to care. But because this is Python, the caller still often does need >> to >> care. > >> The only circumstance where a caller would *never* need to care would be >> when >> the object returned could be guaranteed to *never ever* have any methods >> called >> on it that weren't part of the interface defined by the interface being >> looked >> up. If we want this, we should probably just be using Java. > > I think it's okay for us to say: if you call a method on something that > you retrieved by interface that isn't actually specified to be on that > interface, you're on your own. It may work, but it's not specified to > work by the system you're using.
Let's use an analogy here. You are a member of a club. This club is very inclusive, but it has a fairly rigid set of protocols that members are expected to follow. For example, when you enter the front door, you are expected to touch a statue next the the door. When a bell rings, you are expected to sing the club song. At precisely 1pm, everyone must kneel in place. A number of other arbitrary actions are required of a new member, and new ones are added every so often. This club is slightly superstitious, so it is believed that *not* performing the required actions is bad luck, and any member who does not perform them "to the letter" will bring a pox upon the club and all its other members. In fact, the club members have incontrovertable evidence that this is so. The club currently has a membership in the hundreds. They want to be inclusive, so they welcome anybody, and they're glad to get new members. But those who come in are expected to read a slightly tattered printout of a PowerPoint presentation hanging from a string on the inside of the front door. This printout contains all the known actions that a member must perform in response to cues to be a member in good standing. New members can also ask existing members for help, of course, and they can go visit the printout any time. If everyone follows the set of instructions in the powerpoint document to the letter, everything runs swimmingly. But the powerpoint document is missing some pages, (some new cues were added recently) and even if it wasn't, new members would almost always have a hard time getting it *all* right on the first try because there are a lot of cues these days. The bell rings, they kneel. They sing the song at the wrong time. Sometimes they just don't know what to do, so they do nothing. The club also has a wait staff. Currently the wait staff is not expected to enforce any of the rules; they simply keep things running. They are severely underworked at the moment. Currently the club interpersonals are a little broken: when a new member has the wrong response to a cue, the existing club members might try to help that new member, but often they just complain to each other about the offending new member. Some members just claim "I've already paid my dues to be a member here, he should too." Other members try to write more little sticky notes and hang them up as cues to new members, but nobody is really "responsible" for teaching new members the rules. It's sink or swim, and often new members sink. If the the "club" is the "ZCA community", the "cues" are the worldview that says that the ZCA is by god a way to convert one interface to another and that utility and adapter lookups are no different than each other, the wait staff is the ZCA itself, new members are new developers, and the mistakes they make when not following the cues using methods of the adapted objects that aren't defined by the adapter interface: 1) Put the wait staff in charge of helping new members. This is analogous to making the ZCA throw (or at least warn) about errors that new developers make by using attributes and methods of an object not defined by the interface returned by an adapter or utility lookup. 2) Get rid of most of the cues. This is analogous to "unifying" adapter lookup and utility lookup by making the *caller* responsible for doing "adaptation" by calling the result of the lookup himself. Or do nothing. Doing nothing is wrong because at some point *we* become maintainers of code written by folks that "didn't get the jokes" of the ZCA worldview. So we can either take the joke out or make the joke impossible to miss. > I'm not sure why lifetime issues are considered so important here. Of > course they sometimes matter. But we use ORMs all the time. We create > traversal hierarchies on the fly all the time. What's different in this > case? > > If I want to index an object, do I care whether the catalog was just > created on the fly and it's talking to a relational database? > > I think you're arguing that because abstractions are sometimes leaky > (lifetime issues) we shouldn't have this abstraction at all. Why is the > leakiness so important here? *This* abstraction is leaky. Other ones which have been proposed are not. Or at least not as. > I think there are two centers of attraction for unification: > > * everything is a factory. If I want to look up an instance, I look up a > factory that returns me the same instance all the time. I.e. IFoo() and > IFoo(a) > > * everything is an instance. If I want to call a factory, I look up an > instance that is a factory, and call it. I.e. IFoo.lookup() and > IFoo.lookup(a)(a) I think either of these positions is probably consistent. > zope.component presents a compromise between those positions. I don't know that the documenting the compromise as a worldview is particularly useful, given that any reasonable Python programmer can add back the compromise abstractions with about 10-20 lines of his own code should he want to. > The advantage I see of unifying towards factories is a less verbose API > for common operations than an explicit API would have. > > If you take the utilities come from a factory approach, this would > suggest an API like this (on Interface): > > def __call__(self, arg=()): > if not isinstance(arg, tuple): > arg = (arg,) > return self.factory(arg)(*arg) > > # low level > def factory(self, arg=()): > return registry.lookup(from_=arg, to=self) > > The drawback is that while you *can* register utilities as non-factories > and look them up, you'd do it through 'factory', as the real way to look > up utilities would be to call a factory. > > If you take the utilities are registered instances, that would suggest > an API like this: > > # convenience > def __call__(self, arg=()): > if not isinstance(arg, tuple): > arg = (arg,) > return self.lookup(arg)(*arg) > > def lookup(self, arg=()): > return registry.lookup(from_=arg, to=self) > > You'd register utilities as instances directly and look them up using > lookup(). The convenience method still works for adapters. > > The drawback is that IFoo() would result in IFoo.lookup()(), and calling > the utility would make no sense. > > The implementations are the same, but the semantics are quite different. :) I will try to understand this more later. - C > > Regards, > > Martijn > > _______________________________________________ > Zope-Dev maillist - Zope-Dev@zope.org > https://mail.zope.org/mailman/listinfo/zope-dev > ** No cross posts or HTML encoding! ** > (Related lists - > https://mail.zope.org/mailman/listinfo/zope-announce > https://mail.zope.org/mailman/listinfo/zope ) > _______________________________________________ Zope-Dev maillist - Zope-Dev@zope.org https://mail.zope.org/mailman/listinfo/zope-dev ** No cross posts or HTML encoding! ** (Related lists - https://mail.zope.org/mailman/listinfo/zope-announce https://mail.zope.org/mailman/listinfo/zope )