Re: [Python-Dev] PEP 246, redux
On Fri, 14 Jan 2005 08:50:27 +0100, Alex Martelli [EMAIL PROTECTED] wrote: Ooops -- sorry; I wouldn't have imagined Brazilian hits would swamp the google hits to that extent, mostly qualifying post-grad courses and the like... seems to be an idiom there for that. 'Lato sensu' is used to indicate short post-graduate level courses that don't give one any recognized degree such as 'MSc', or 'master'. It's pretty much like a specialization course on some specific area, usually offered by small private universities. It's like a fever around here - everyone does just to add something to the resume - and has spawned a entire branch in the educational industry (and yeah, 'industry' is the best word for it). Some schools refer to traditional post graduate courses as 'stricto sensu'. I don't have the slightest idea about where they did get this naming from. It's also amazing how many hits you'll get for the wrong spelling: 'latu sensu' 'strictu sensu', mostly from Brazil, and also from some spanish-speaking countries. Also, a reflection: taxonomy, the classification of things (living beings, rocks, legal precedents, ...) into categories, is a discipline with many, many centuries of experience behind it. I think it is telling that taxonomists found out they require _two_ kinds of ``inheritance'' to do their job (no doubt there are all kind of _nuances_, but specialized technical wording exists for two kinds: strict-sense and broad-sense)... they need to be able to assert that A is a B _broadly speaking_ (or specifically _strictly speaking_) so often that they evolved specific terminology. Let's hope it doesn't take OOP many centuries to accept that both stricto sensu inheritance (Liskovianly-correct) AND lato sensu inheritance are needed to do _our_ jobs!-) Good point! -- Carlos Ribeiro Consultoria em Projetos blog: http://rascunhosrotos.blogspot.com blog: http://pythonnotes.blogspot.com mail: [EMAIL PROTECTED] mail: [EMAIL PROTECTED] ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 12, at 21:42, Phillip J. Eby wrote: ... Anyway, hopefully this post and the other one will be convincing that considering ambiguity to be an error *reinforces* the idea of I-to-I perfection, rather than undermining it. (After all, if you've written a perfect one, and there's already one there, then either one of you is mistaken, or you are wasting your time writing one!) I'd just like to point out, as apparently conceded in your fair enough sentence in another mail, that all of this talk of wasting your time writing is completely unfounded. Since that fair enough of yours was deeply buried somewhere inside this huge conversation, some readers might miss the fact that your numerous repetitions of the similar concept in different words are just invalid, because, to recap: Given four interfaces A, B, C, D, there may be need of each of the single steps A-B, A-C, B-D, C-D. Writing each of these four adapters can IN NO WAY be considered wasting your time writing one, because there is no way a set of just three out of the four can be used to produce the fourth one. The only redundancy comes strictly because of transitivity being imposed automatically: at the moment the fourth one of these four needed adapters gets registered, there appear to be two same-length minimal paths A-x-D (x in {B, C}). But inferring _from this consequence of transitivity_ that there's ANYTHING wrong with any of the four needed adapters is a big unwarranted logical jump -- IF one really trusted all interface-interface adapters to be perfect, as is needed to justify transitivity and as you here claims gets reinforced (?!). Thinking of it as redundancy is further shown to be fallacious because the only solution, if each of those 4 adapters is necessary, is to write and register a FIFTH one, A-D directly, even if one has no interest whatsoever in A-D adaptation, just to shut up the error or warning (as you say, there may be some vague analogy to static typing here, albeit in a marginal corner of the stage rather than smack in the spotlight;-). Yes, there is (lato sensu) non-determinism involved, just like in, say: for k in d: print k for a Python dictionary d - depending on how d was constructed and modified during its lifetime (which may in turn depend on what order modules were imported, etc), this produces different outputs. Such non-determinism may occasionally give some problems to unwary programmers (who could e.g. expect d1==d2 -- repr(d1)==repr(d2) when keys and values have unique repr's: the right-pointing half of this implication doesn't hold, so using repr(d) to stand in for d when you need, e.g., a set of dictionaries, is not quite sufficient); such problems at the margin appear to be generally considered acceptable, though. I seems to me that you do at least feel some unease at the whole arrangement, given that you say this whole debate has made me even less enamored of adaptation, as it's not clear to me that any _other_ aspect of this whole debate was quite as problematic (e.g. issues such as how to best get a special method from class rather than instance -- while needing to be resolved for adaptation just as much as for copy.py etc -- hardly seem likely to have been the ones prompting you to go looking for a cleaner, more intuitive way to do it outside of the canonical, widespread approach to OOP). Anyway -- I'm pointing out that what to put in a rewrite of PEP 246 as a result of all this is anything but obvious at this point, at least to me. Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 10:35 AM 1/13/05 +, Paul Moore wrote: Now, a lot of the talk has referred to implicit adaptation. I'm still struggling to understand how that concept applies in practice, beyond the case of adaptation chains - at some level, all adaptation is explicit, insofar as it is triggered by an adapt() call. It's implicit in that the caller of the code that contains the adapt() call carries no visible indication that adaptation will take place. It's *still* not intuitively incorrect to me, but there's a couple things I can think of... (a) After you adapted the path to the file, and have a side-effect of opening a file, it's unclear who is responsible for closing it. (b) The file object clearly has state the path object doesn't have, like a file position. (c) You can't go adapting the path object to a file whenever you wanted, because of those side effects. In the context of my example above, I was assuming that C was an interface (whatever that might be). Here, you're talking about adapting to a file (a concrete class), which I find to be a much muddier concept. This is very much a best practices type of issue, though. I don't see PEP 246 mandating that you *cannot* adapt to concrete classes, but I can see that it's a dangerous thing to do. Even the string-path adaptation could be considered suspect. Rather, you should be defining an IPath *interface*, with operations such as join, basename, and maybe open. Then, the path class would have a trivial adaptation to IPath, and adapting a string to an IPath would likely do so by constructing a path object from the string. From a practical point of view, the IPath interface adds nothing over adapting direct to the path class, but for the purposes of clarity, documentation, separation of concepts, etc, I can see the value. This confusion was another reason for the Duck-Typing Adaptation proposal; it's perfectly fine to take a 'path' class and duck-type an interface from it: i.e when you adapt to 'path', then if you call 'basename' on the object, you will either: 1. Invoke a method that someone has claimed is semantically equivalent to path.basename, OR 2. Get a TypeError indicating that the object you're using doesn't have such an operation available. In effect, this is the duck-typing version of a Java cast: it's more dynamic because it doesn't require you to implement all operations up front, and also because third parties can implement the operations and add them, and because you can define abstract operations that can implement operations in terms of other operations. Some mistakes are easier to avoid if you have the correct conceptual framework. I suspect that interfaces are the conceptual framework which make adaptation fall into place. If so, then PEP 246, and adaptation per se, is always going to be hard to reason about for people without a background in interfaces. Exactly, and that's a problem -- so, I think I've invented (or reinvented, one never knows) the concept of a duck interface, that requires no special background to understand or use, because (for example) it has no inheritance except normal inheritance, and involves no adapter classes anywhere. Therefore, the reasoning you already apply to ordinary Python classes just works. (Versus e.g. the interface-logic of Zope and PyProtocols, which is *not* ordinary Python inheritance.) Hmm. I think I just disqualified myself from making any meaningful comments :-) And I just requalified you. Feel free to continue commenting. :) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 09:34 AM 1/13/05 -0500, Clark C. Evans wrote: On Thu, Jan 13, 2005 at 10:35:39AM +, Paul Moore wrote: | One thing I feel is key is the fact that adaptation is a *tool*, and | as such will be used in different ways by different people. That is | not a bad thing, even if it does mean that some people will abuse the tool. | | Now, a lot of the talk has referred to implicit adaptation. I'm | still struggling to understand how that concept applies in practice, | beyond the case of adaptation chains - at some level, all adaptation | is explicit, insofar as it is triggered by an adapt() call. The 'implicit' adaptation refers to the automagical construction of composite adapters assuming that a 'transitive' property holds. Maybe some folks are using the term that way; I use it to mean that in this code: someOb.something(aFoo) 'aFoo' may be implicitly adapted because the 'something' method has a type declaration on the parameter. Further, 'something' might call another method with another type declaration, passing the adapted version of 'foo', which results in you possibly getting implicit transitive adaptation *anyway*, without having intended it. Also, if adapters have per-adapter state, and 'someOb.something()' is expecting 'aFoo' to keep some state it puts there across calls to methods of 'someOb', then this code won't work correctly. All of these things are implicit adaptation issues, IMO, and exist even withoutPyProtocols-style transitivity. Duck adaptation solves these issues by prohibiting per-adapter state and by making adaptation order-insensitive. (I.e. adapt(adapt(a,B),C) should always produce the same result as adapt(a,C).) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On Thu, Jan 13, 2005 at 08:40:56PM +0100, Alex Martelli wrote: | The other issue with registries (and why I avoided them in the | origional) is that they often require a scoping; in this case, | the path taken by one module might be different from the one | needed by another. | | I think that should not be supported, just like different modules | cannot register different ways to copy.copy(X) for the same X. One | size had better fit all, be it a single specific adapter or potentially | a path thereof. Sounds good. | Ok. I just think you all are solving a problem that doesn't exist, | | Apparently, the existence of the problem is testified by the experience | of the Eclipse developers (who are, specifically, adapting plugins: | Eclipse being among the chief examples f plugin-based architecture... | definitely an N-players scenario). Some specific examples from Eclipse developers would help then, especially ones that argue strongly for automagical transitive adaptation. That is, ones where an alternative approach that is not automatic is clearly inferior. |A component developer X and a framework developer Y both |have stuff that an application developer A is putting |together. The goal is for A to not worry about _how_ the |components and the framework fit; to automatically find |the glue code. ... | I'm quite ready to consider the risk of having too-thick combined | layers of glue resulting from adaptation (particularly but not | exclusively with transitivity): indeed PJE's new ideas may be seen as a | novel way to restart-from-scratch and minimize glue thickness in the | overall resulting concoction. I do like PJE's idea, since it seems to focus on declaring individual functions rather than on sets of functions; but I'm still unclear what problem it is trying to solve. | But the optional ability for | particularly skilled glue-layers to have that extra layer which makes | everything better should perhaps not be discounted. Although, | considering PJE's new just-started effort, it may well be wisest for | PEP 246 to stick to a minimalist attitude -- leave open the possibility | of future additions or alterations but only specify that minimal core | of functionality which we all _know_ is needed. I'd rather not be pushing for a powerful regsistry mechansim unless we have solid evidence that the value it provides outweighs the costs that it incurres. | I strongly disagree; the most useful adapters are the ones that | discard unneeded information. | | The Facade design pattern? It's useful, but I disagree that it's most | useful when compared to general Adapter My qualification was not very well placed. That said, I don't see any reason why a facade can't also be asked for via the adapt() mechanism. | So please explain what's imperfect in wrapping a str into a StringIO? It adds information, and it implies mutability which the underlying object is not. In short, it's quite a different animal from a String, which is why String-StringIO is a great example for an adapter. | | What about registered explicitly as being suitable for transitivity, | | would that suffice? | | I suppose so. But I think it is a bad idea for a few reasons: | | 1. it seems to add complexity without a real-world justifcation, | let's go without it; and add it in a later version if it turns | out to be as valueable as people think | | Particularly in the light of PJE's newest ideas, being spare and | minimal in PEP 246 does sound good, as long as we're not shutting and | bolting doors against future improvements. Agreed! | 2. different adapters have different intents... | | If you've ever looked into quality of data issues in huge databases, | you know that these are two (out of thousands) typical problems -- but | not problems in _adaptation_, in fact. I deal with these issues all of the time; but what I'm trying to express with the example is that someone may _think_ that they are writing a perfect adapter; but they may be wrong, on a number of levels. It's not so much to say what is good, but rather to challenge the notion of a 'perfect adapter'. | In short, unless a human is giving the 'ok' to an adapter's | use, be it the application, framework, or component developer, | then I'd expect wacko bugs. | | A lot of the data quality problems in huge databases come exactly from | humans -- data entry issues, form design issues, ... all the way to | schema-design issues. I don't see why, discussing a data-quality | problem, you'd think that having a human OK whatsoever would help wrt | having a formalized rule (e.g. a database constraint) do it. The point I was trying to make is that automatically constructing adapters isn't a great idea unless you have someone who can vouche for the usefulness. In other words, I picture this as a physics story problem, where a bunch of numbers are given with units. While the units may keep you in check,
Re: [Python-Dev] PEP 246, redux
On Thu, 13 Jan 2005 13:43:53 -0800, Paramjit Oberoi [EMAIL PROTECTED] wrote: On Thu, 13 Jan 2005 20:40:56 +0100, Alex Martelli [EMAIL PROTECTED] wrote: So please explain what's imperfect in wrapping a str into a StringIO? If I understand Philip's argument correctly, the problem is this: def print_next_line(f: file): print f.readline() s = line 1\n line 2 print_next_line(s) print_next_line(s) This will print line 1 twice. Nice example! The real subtlety here is that f = adapt(s, StringIO) print_next_line(f) print_next_line(f) *does* work - the implication is that for the original example to work, adapt(s, StringIO) needs to not only return *a* wrapper, but to return *the same wrapper* every time. Which may well break *other* uses, which expect a new wrapper each time. But the other thing that this tends to make me believe even more strongly is that using Guido's type notation for adaptation is a bad thing. def print_next_line(f): ff = adapt(f, file) print ff.readline() Here, the explicit adaptation step in the definition of the function feels to me a little more obviously a wrapping operation which may reinitialise the adapter - and would raise warning bells in my mind if I thought of it in terms of a string-StringIO adapter. Add this to the inability to recover the original object (for readaptation, or passing on as an argument to another function), and I'm very concerned about Guido's type notation being used as an abbreviation for adaptation... Paul. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On Jan 13, 2005, at 12:46 PM, Clark C. Evans wrote: My current suggestion to make 'transitive adaption' easy for a application builder (one putting togeher components) has a few small parts: - If an adaptation is not found, raise an error, but list in the error message two additional things: (a) what possible adaptation paths exist, and (b) how to register one of these paths in their module. - A simple method to register an adaption path, the error message above can even give the exact line needed, adapt.registerPath(from=A,to=C,via=B) I'd just like to note that this won't solve my use case for transitive adaptation. To keep backwards compatibility, I can't depend on the application developer to register an adapter path from A through IResource to INewResource. Trying to adapt A to INewResource needs to just work. I can't register the path either, because I (the framework author) don't know anything about A. A solution that would work is if I have to explicitly declare the adapter from IResource to INewResource as 'safe', as long as I don't also have to declare the adapter from A to IResource as 'safe'. (That is, I suppose -- in a transitive adapter chain, all except one adapter in the chain would have to be declared 'safe'). I don't know whether or not it's worthwhile to have this encoded in the framework, as it is clearly possible to do it on my own in any case. I'll leave that for others to debate. :) James ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 14, at 04:08, David Ascher wrote: Alex Martelli wrote: Yes, there is (lato sensu) non-determinism involved, just like in, say: for k in d: print k Wow, it took more than the average amount of googling to figure out that lato sensu means broadly speaking, Ooops -- sorry; I wouldn't have imagined Brazilian hits would swamp the google hits to that extent, mostly qualifying post-grad courses and the like... seems to be an idiom there for that. and occurs as sensu lato with a 1:2 ratio. In Latin as she was spoken word order is very free, but the issue here is that _in taxonomy specifically_ (which was the way I intended the form!) the sensu lato order vastly predominates. Very exhaustive discussion of this word order choice in taxonomy at http://www.forum-one.org/new-1967018-4338.html, btw (mostly about sensu scricto, the antonym). I learned something today! ;-) Me too: about Brazilian idiom, and about preferred word-order use in Aquinas and Bonaventura. Also, a reflection: taxonomy, the classification of things (living beings, rocks, legal precedents, ...) into categories, is a discipline with many, many centuries of experience behind it. I think it is telling that taxonomists found out they require _two_ kinds of ``inheritance'' to do their job (no doubt there are all kind of _nuances_, but specialized technical wording exists for two kinds: strict-sense and broad-sense)... they need to be able to assert that A is a B _broadly speaking_ (or specifically _strictly speaking_) so often that they evolved specific terminology. Let's hope it doesn't take OOP many centuries to accept that both stricto sensu inheritance (Liskovianly-correct) AND lato sensu inheritance are needed to do _our_ jobs!-) Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
Clark C. Evans wrote: On Tue, Jan 11, 2005 at 12:54:36PM -0500, Phillip J. Eby wrote: ... | * In my experience, incorrectly deriving an interface from another is the | most common source of unintended adaptation side-effects, not adapter | composition It'd be nice if interfaces had a way to specify a test-suite that could be run against a component which claims to be compliant. For example, it could provide invalid inputs and assert that the proper errors are returned, etc. We've tried this in Zope 3 with very limited success. In fact, so far, our attempts have provided more pain than their worth. The problem is that interfaces are usually abstract enough that it's very difficult to write generic tests. For example, many objects implement mapping protocols, but place restrictions on the values stored. It's hard to provide generic tests that don't require lots of inconvenient hooks. There are exceptions of course. Our ZODB storage tests use a generic storage-interface test, but this is possible because the ZODB storage interfaces are extremely concrete. Jim -- Jim Fulton mailto:[EMAIL PROTECTED] Python Powered! CTO (540) 361-1714http://www.python.org Zope Corporation http://www.zope.com http://www.zope.org ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 12, at 14:44, Paul Moore wrote: On Wed, 12 Jan 2005 00:33:22 +0100, Alex Martelli [EMAIL PROTECTED] wrote: But adaptation is not transmission! It's PERFECTLY acceptable for an adapter to facade: to show LESS information in the adapted object than was in the original. It's PERFECTLY acceptable for an adapter to say this piece information is not known when it's adapting an object for which that information, indeed, is not known. It's only CONJOINING the two perfectly acceptable adapters, as transitivity by adapter chain would do automatically, that you end up with a situation that is pragmatically undesirable: asserting that some piece of information is not known, when the information IS indeed available -- just not by the route automatically taken by the transitivity-system. [Risking putting my head above the parapet here :-)] If you have adaptations A-B, B-C, and A-C, I would assume that the system would automatically use the direct A-C route rather than A-B-C. I understand that this is what PyProtocols does. Yes, it is. Are you mistakenly thinking that shortest-possible-route semantics aren't used? Maybe the PEP should explicitly require such semantics. No, I'm not. I'm saying that if, by mistake, the programmer has NOT registered the A-C adapter (which would be easily coded and work perfectly), then thanks to transitivity, instead of a clear and simple error message leading to immediate diagnosis of the error, they'll get a subtle unnecessary degradation of information and resulting reduction in information quality. PyProtocols' author claims this can't happen because if adapters A-B and B-C are registered then each adapter is always invariably claiming to be lossless and perfect. However, inconsistently with that stance, I believe that PyProtocols does give an error message if it finds two adaptation paths of equal minimum length, A-B-C or A-Z-C -- if it is truly believed that each adaptation step is lossless and perfect, it's inconsistent to consider the existence of two equal-length paths an error... either path should be perfect, so just picking either one of them should be a perfectly correct strategy. If I'm missing the point here, I apologise. But I get the feeling that something's getting lost in the discussions. The discussions on this subject always and invariably get extremely long (and often somewhat heated, too), so it's quite possible that a lot is getting lost along the way, particularly to any other reader besides the two duelists. Thus, thanks for focusing on one point that might well be missed by other readers (though not by either PJE or me;-) and giving me a chance to clarify it! Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 02:27 PM 1/12/05 +, Mark Russell wrote: I strongly prefer *not* to have A-B and B-C automatically used to construct A-C. Explicit is better than implicit, if in doubt don't guess, etc etc. So I'd support: - As a first cut, no automatic transitive adaptation - Later, and only if experience shows there's a need for it, Well, by the experience of the people who use it, there is a need, so it's already later. :) And at least my experience *also* shows that transitive interface inheritance with adaptation is much easier to accidentally screw up than transitive adapter composition -- despite the fact that nobody objects to the former. But if you'd like to compare the two approaches pragmatically, try using both zope.interface and PyProtocols, and see what sort of issues you run into. They have pretty much identical interface syntax, and you can use the PyProtocols declaration API and 'adapt' function to do interface declarations for either Zope interfaces or PyProtocols interfaces -- and the adaptation semantics follow Zope if you're using Zope interfaces. So, you can literally flip between the two by changing where you import the 'Interface' class from. Both Zope and PyProtocols support the previous draft of PEP 246; the new draft adds only two new features: * Ability for a class to opt out of the 'isinstance()' check for a base class (i.e., for a class to say it's not substitutable for its base class, for Alex's private inheritance use case) * Ability to have a global type-protocol adapter registry Anyway, I'm honestly curious as to whether anybody can find a real situation where transitive adapter composition is an *actual* problem, as opposed to a theoretical one. I've heard a lot of people talk about what a bad idea it is, but I haven't heard any of them say they actually tried it. Conversely, I've also heard from people who *have* tried it, and liked it. However, at this point I have no way to know if this dichotomy is just a reflection of the fact that people who don't like the idea don't try it, and the people who either like the idea or don't care are open to trying it. The other thing that really blows my mind is that the people who object to the idea don't get that transitive interface inheritance can produce the exact same problem, and it's more likely to happen in actual *practice*, than it is in theory. As for the issue of what should and shouldn't exist in Python, it doesn't really matter; PEP 246 doesn't (and can't!) *prohibit* transitive adaptation. However, I do strongly object to the spreading of theoretical FUD about a practical, useful technique, much as I would object to people saying that using significant whitespace is braindead who had never tried actually using Python. The theoretical problems with transitive adapter composition are in my experience just as rare as whitespace-eating nanoviruses from outer space. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
[Alex] I'm saying that if, by mistake, the programmer has NOT registered the A-C adapter (which would be easily coded and work perfectly), then thanks to transitivity, instead of a clear and simple error message leading to immediate diagnosis of the error, they'll get a subtle unnecessary degradation of information and resulting reduction in information quality. I understand, but I would think that there are just as many examples of cases where having to register a trivial A-C adapter is much more of a pain than it's worth; especially if there are a number of A-B pairs and a number of B-C pairs, the number of additional A-C pairs needed could be bewildering. But I would like to see some input from people with C++ experience. C++ goes to great lengths to pick automatic conversions (which perhaps aren't quite the same as adaptations but close enough for this comparison to work) and combine them. *In practice*, is this a benefit or a liability? -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 12, at 16:12, Phillip J. Eby wrote: At 02:27 PM 1/12/05 +, Mark Russell wrote: I strongly prefer *not* to have A-B and B-C automatically used to construct A-C. Explicit is better than implicit, if in doubt don't guess, etc etc. So I'd support: - As a first cut, no automatic transitive adaptation - Later, and only if experience shows there's a need for it, Well, by the experience of the people who use it, there is a need, so it's already later. :) And at least my experience *also* shows that transitive interface inheritance with adaptation is much easier to accidentally screw up than transitive adapter composition -- despite the fact that nobody objects to the former. A-hem -- I *grumble* about the former (and more generally the fact that inheritance is taken as so deucedly *committal*:-). If it doesn't really count as a complaint it's only because I doubt I can do anything about it and I don't like tilting at windmills. But, I _DO_ remember Microsoft's COM, with inheritance of interface *NOT* implying anything whatsoever (except the fact that the inheriting one has all the methods of the inherited one with the same signature, w/o having to copy and paste, plus of course you can add more) -- I remember that idea with fondness, as I do many other features of a components-system that, while definitely not without defects, was in many respects a definite improvement over the same respects in its successors. The other thing that really blows my mind is that the people who object to the idea don't get that transitive interface inheritance can produce the exact same problem, and it's more likely to happen in actual *practice*, than it is in theory. Believe me, I'm perfectly glad to believe that [a] implied transitivity in any form, and [b] hypercommittal inheritance, cause HUGE lots of problems; and to take your word that the combination is PARTICULARLY bug-prone in practice. It's just that I doubt I can do anything much to help the world avoid that particular blight. As for the issue of what should and shouldn't exist in Python, it doesn't really matter; PEP 246 doesn't (and can't!) *prohibit* transitive adaptation. However, I do strongly object to the spreading of theoretical FUD about a practical, useful technique, much as I would object to people saying that using significant whitespace is braindead who had never tried actually using Python. The theoretical problems with transitive adapter composition are in my experience just as rare as whitespace-eating nanoviruses from outer space. Well, I'm not going to start real-life work on a big and complicated system (the kind where such problems would emerge) relying on a technique I'm already dubious about, if I have any say in the matter, so of course I'm unlikely to gain much real-life experience -- I'm quite content, unless somebody should be willing to pay me adequately for my work yet choose to ignore my advice in the matter;-), to rely on imperfect analogies with other experiences based on other kinds of unwanted and unwarranted but uncontrollable and unstoppable applications of transitivity by underlying systems and frameworks. I already know -- you told us so -- that if I had transitivity as you wish it (uncontrollable, unstoppable, always-on) I could not any more write and register a perfectly reasonable adapter which fills in with a NULL an optional field in the adapted-to interface, without facing undetected degradation of information quality by that adapter being invisibly, uncontrollably chained up with another -- no error message, no nothing, no way to stop this -- just because a direct adapter wasn't correctly written and registered. Just this detail, for me, is reason enough to avoid using any framework that imposes such noncontrollable transitivity, if I possibly can. Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
getattr and __mro__ (was Re: [Python-Dev] PEP 246, redux)
Armin Rigo [EMAIL PROTECTED] writes: ... is that the __adapt__() and __conform__() methods should work just like all other special methods for new-style classes. The confusion comes from the fact that the reference implementation doesn't do that. It should be fixed by replacing: conform = getattr(type(obj), '__conform__', None) with: for basecls in type(obj).__mro__: if '__conform__' in basecls.__dict__: conform = basecls.__dict__['__conform__'] break else: # not found and the same for '__adapt__'. The point about tp_xxx slots is that when implemented in C with slots, you get the latter (correct) effect for free. This is how metaconfusion is avoided in post-2.2 Python. Using getattr() for that is essentially broken. Trying to call the method and catching TypeErrors seems pretty fragile -- e.g. if you are calling a __conform__() which is implemented in C you won't get a Python frame in the traceback either. I'm confused. Do you mean that getattr(obj, somemethod)(...) does something different than obj.somemethod(...) with new style class instances? Doesn't getattr search the __dict__'s along the __mro__ list? Thomas ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
[Alex] Of course, it's possible that some such wrappers are coded much more tighter c, so that in fact some roundabout A - X1 - X2 - C would actually be better performing than either A - B - C or A - Z - C, but using one of the shortest available paths appears to be a reasonable heuristic for what, if one assumes away any degradation, is after all a minor issue. I would think that the main reason for preferring the shortest path is the two degenerate cases, A-A (no adaptation necessary) and A-C (a direct adapter is available). These are always preferable over longer possibilities. Demanding that the set of paths of minimal available length has exactly one element is strange, though, I think you're over-emphasizing this point (in several messages); somehow you sound a bit like you're triumphant over having found a bug in your opponent's reasoning. [...] So, yes, I'd _also_ love to have two grades of inheritance, one of the total commitment kind (implying transitivity and whatever), and one more earthly a la ``I'm just doing some convenient reuse, leave me alone''. I'll bet that the list of situations where occasionally you wish you had more control over Python's behavior is a lot longer than that, and I think that if we started implementing that wish list (or anybody's wish list), we would soon find that we had destroyed Python's charming simplicity. My personal POV here: even when you break Liskov in subtle ways, there are lots of situations where assuming substitutability has no ill effects, so I'm happy to pretend that a subclass is always a subtype of all of its base classes, (and their base classes, etc.). If it isn't, you can always provide an explicit adapter to rectify things. As an example where a subclass that isn't a subtype can be used successfully, consider a base class that defines addition to instances of the same class. Now consider a subclass that overrides addition to only handle addition to instances of that same subclass; this is a Liskov violation. Now suppose the base class also has a factory function that produces new instances, and the subclass overrides this to produce new instances of the subclass. Then a function designed to take an instance of the base class and return the sum of the instances produced by calling the factory method a few times will work perfectly with a subclass instance as argument. Concrete: class B: def add(self, other: B) - B: ... def factory(self) - B: ... class C(B): def add(self, other: C) - C: ... # other: C violates Liskov def factory(self) - C: ... def foo(x: B) - B: x1 = x.factory() x2 = x.factory() return x1.add(x2) This code works fine in today's python if one leaves the type declarations out. I don't think anybody is served by forbidding it. -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
[Phillip] As for the issue of what should and shouldn't exist in Python, it doesn't really matter; PEP 246 doesn't (and can't!) *prohibit* transitive adaptation. Really? Then isn't it underspecified? I'd think that by the time we actually implement PEP 246 in the Python core, this part of the semantics should be specified (at least the default behavior, even if there are hooks to change this). -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 12, at 16:26, Guido van Rossum wrote: ... [Alex] I'm saying that if, by mistake, the programmer has NOT registered the A-C adapter (which would be easily coded and work perfectly), then thanks to transitivity, instead of a clear and simple error message leading to immediate diagnosis of the error, they'll get a subtle unnecessary degradation of information and resulting reduction in information quality. I understand, but I would think that there are just as many examples of cases where having to register a trivial A-C adapter is much more of a pain than it's worth; especially if there are a number of A-B pairs and a number of B-C pairs, the number of additional A-C pairs needed could be bewildering. Hm? For any A and B there can be only one A-B adapter registered. Do you mean a number of A-B1, B1-C1 ; A-B2, B2-C2; etc? Because if it was B1-C and B2-C, as I understand the transitivity of PyProtocols, it would be considered an error. But I would like to see some input from people with C++ experience. Here I am, at your service. I've done, taught, mentored, etc, much more C++ than Python in my life. I was technical leader for the whole C - C++ migration of a SW house which at that time had more than 100 programmers (just as I had earlier been for the Fortran - C migration back a few years previously, with around 30 programmers): I taught internal courses, seminars and workshops on C++, its differences from C, OO programming and design, Design Patterns, and later generic programming, the STL, and so on, and so forth. I mentored a lot of people (particularly small groups of people that would later go and teach/mentor the others), pair-programmed in the most critical migrations across the breadth of that SW house's software base, etc, etc. FWIW, having aced Brainbench's C++ tests (I was evaluating them to see if it would help us select among candidates claiming C++ skills), I was invited to serve for a while as one of their Most Valued Professionals (MVPs) for C++, and although I had concluded that for that SW house's purposes the tests weren't all that useful, I did, trying to see if I could help make them better (more suitable to test _real-world_ skills and less biased in favour of people with that language-lawyer or library-packrat kind of mentality I have, which is more useful in tests than out in the real world). I hope I can qualify as a C++ expert by any definition. C++ goes to great lengths to pick automatic conversions (which perhaps aren't quite the same as adaptations but close enough for this comparison to work) I agree with you, though I believe PJE doesn't (he doesn't accept my experience with such conversions as a valid reason for me to be afraid of close enough for this comparison adaptations). and combine them. *In practice*, is this a benefit or a liability? It's in the running for the coveted Alex's worst nightmare prize, with a few other features of C++ - alternatively put, the prize for reason making Alex happiest to have switched to Python and _almost_ managed to forget C++ save when he wakes up screaming in the middle of the night;-). Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: getattr and __mro__ (was Re: [Python-Dev] PEP 246, redux)
[Armin] ... is that the __adapt__() and __conform__() methods should work just like all other special methods for new-style classes. The confusion comes from the fact that the reference implementation doesn't do that. It should be fixed by replacing: conform = getattr(type(obj), '__conform__', None) with: for basecls in type(obj).__mro__: if '__conform__' in basecls.__dict__: conform = basecls.__dict__['__conform__'] break else: # not found and the same for '__adapt__'. The point about tp_xxx slots is that when implemented in C with slots, you get the latter (correct) effect for free. This is how metaconfusion is avoided in post-2.2 Python. Using getattr() for that is essentially broken. Trying to call the method and catching TypeErrors seems pretty fragile -- e.g. if you are calling a __conform__() which is implemented in C you won't get a Python frame in the traceback either. [Thomas] I'm confused. Do you mean that getattr(obj, somemethod)(...) does something different than obj.somemethod(...) with new style class instances? Doesn't getattr search the __dict__'s along the __mro__ list? No, he's referring to the (perhaps not widely advertised) fact that obj[X] is not quite the same as obj.__getitem__(X) since the explicit method invocation will find obj.__dict__[__getitem__] if it exists but the operator syntax will start the search with obj.__class__.__dict__. -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: getattr and __mro__ (was Re: [Python-Dev] PEP 246, redux)
On 2005 Jan 12, at 16:44, Thomas Heller wrote: ... conform = getattr(type(obj), '__conform__', None) ... I'm confused. Do you mean that getattr(obj, somemethod)(...) does something different than obj.somemethod(...) with new style class instances? Doesn't getattr search the __dict__'s along the __mro__ list? Yes, but getattr(obj, ... ALSO searches obj itself, which is what we're trying to avoid here. getattr(type(obj), ... OTOH has a DIFFERENT problem -- it ALSO searches type(type(obj)), the metaclass, which we DON'T want. Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 12, at 17:40, Phillip J. Eby wrote: At 04:36 PM 1/12/05 +0100, Alex Martelli wrote: I already know -- you told us so -- that if I had transitivity as you wish it (uncontrollable, unstoppable, always-on) I could not any more write and register a perfectly reasonable adapter which fills in with a NULL an optional field in the adapted-to interface, without facing undetected degradation of information quality by that adapter being invisibly, uncontrollably chained up with another -- no error message, no nothing, no way to stop this -- just because a direct adapter wasn't correctly written and registered. But why would you *want* to do this, instead of just explicitly converting? That's what I don't understand. If I were writing such a converter, I wouldn't want to register it for ANY implicit conversion, even if it was non-transitive! Say I have an SQL DB with a table such as: CREATE TABLE fullname ( first VARCHAR(50) NOT NULL, middle VARCHAR(50), last VARCHAR(50) NOT NULL, -- snipped other information fields ) Now, I need to record a lot of names of people, which I get from a vast variety of sources, so they come in as different types. No problem: I'll just adapt each person-holding type to an interface which offers first, middle and last names (as well as other information fields, here snipped), using None to mean I don't know the middle name for a given person (that's what NULL means, after all: information unknown or the like; the fact that fullname.middle is allowed to be NULL indicates that, while it's of course BETTER to have that information, it's not a semantic violation if that information just can't be obtained nohow). All of my types which hold info on people can at least supply first and last names; some but not all can supply middle names. Fine, no problem: I can adapt them all with suitable adapters anyway, noninvasively, without having to typecheck, typeswitch, or any other horror. Ah, the magic of adaptation! So, I define an interface -- say with arbitrary syntax: interface IFullname: first: str middle: str or None last: str # snipped other information fields and my function to write a data record is just: def writeFullname(person: IFullname): # do the writing So, I have another interface in a similar vein, perhaps to map to/from some LDAP and similar servers which provide a slightly different set of info fields: interface IPerson: firstName: str lastName: str userid: str # snipped other stuff I have some data about people coming in from LDAP and the like, which I want to record in that SQL DB -- the incoming data is held in types that implement IPerson, so I write an adapter IPerson - IFullname for the purpose. If the datatypes are immutable, conversion is as good as adaptation here, as I mentioned ever since the first mail in which I sketched this case, many megabytes back. But adaptation I can get automatically WITHOUT typechecking on what exactly is the concrete type I'm having to write (send to LDAP, whatver) this time -- a crucial advantage of adaptation, as you mention in the PyProtocols docs. Besides, maybe in some cases some of those attributes are in fact properties that get computed at runtime, fetched from a slow link if and only if they're first required, whatever, or even, very simply, some datatype is mutable and I need to ensure I'm dealing with the current state of the object/record. So, I'm not sure why you appear to argue for conversion against adaptation, or explicit typechecking against the avoidance thereof which is such a big part of adapt's role in life. Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 06:18 PM 1/12/05 +0100, Alex Martelli wrote: On 2005 Jan 12, at 17:40, Phillip J. Eby wrote: At 04:36 PM 1/12/05 +0100, Alex Martelli wrote: I already know -- you told us so -- that if I had transitivity as you wish it (uncontrollable, unstoppable, always-on) I could not any more write and register a perfectly reasonable adapter which fills in with a NULL an optional field in the adapted-to interface, without facing undetected degradation of information quality by that adapter being invisibly, uncontrollably chained up with another -- no error message, no nothing, no way to stop this -- just because a direct adapter wasn't correctly written and registered. But why would you *want* to do this, instead of just explicitly converting? That's what I don't understand. If I were writing such a converter, I wouldn't want to register it for ANY implicit conversion, even if it was non-transitive! [snip lots of stuff] I have some data about people coming in from LDAP and the like, which I want to record in that SQL DB -- the incoming data is held in types that implement IPerson, so I write an adapter IPerson - IFullname for the purpose. This doesn't answer my question. Obviously it makes sense to adapt in this fashion, but not IMPLICITLY and AUTOMATICALLY. That's the distinction I'm trying to make. I have no issue with writing an adapter like 'PersonAsFullName' for this use case; I just don't think you should *register* it for automatic use any time you pass a Person to something that takes a FullName. So, I'm not sure why you appear to argue for conversion against adaptation, or explicit typechecking against the avoidance thereof which is such a big part of adapt's role in life. Okay, I see where we are not communicating; where I've been saying conversion, you are taking this to mean, don't write an adapter, but what I mean is don't *register* the adapter for implicit adaptation; explicitly use it in the place where you need it. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 12, at 18:58, Phillip J. Eby wrote: ... I have some data about people coming in from LDAP and the like, which I want to record in that SQL DB -- the incoming data is held in types that implement IPerson, so I write an adapter IPerson - IFullname for the purpose. This doesn't answer my question. Obviously it makes sense to adapt in this fashion, but not IMPLICITLY and AUTOMATICALLY. That's the distinction I'm trying to make. I have no issue with writing an adapter like 'PersonAsFullName' for this use case; I just don't think you should *register* it for automatic use any time you pass a Person to something that takes a FullName. I'm adapting incoming data that can be of any of a huge variety of concrete types with different interfaces. *** I DO NOT WANT TO TYPECHECK THE INCOMING DATA *** to know what adapter or converter to apply -- *** THAT'S THE WHOLE POINT *** of PEP 246. I can't believe we're misunderstanding each other about this -- there MUST be miscommunication going on! So, I'm not sure why you appear to argue for conversion against adaptation, or explicit typechecking against the avoidance thereof which is such a big part of adapt's role in life. Okay, I see where we are not communicating; where I've been saying conversion, you are taking this to mean, don't write an adapter, but what I mean is don't *register* the adapter for implicit adaptation; explicitly use it in the place where you need it. Adaptation is not conversion is how I THOUGHT we had agreed to rephrase my unfortunate adaptation is not casting -- so if you're using conversion to mean adaptation, I'm nonplussed. Needing to be explicit and therefore to typechecking/typeswitching to pick which adapter to apply is just what I don't *WANT* to do, what I don't want *ANYBODY* to have to do EVER, and the very reason I'm spending time and energy on PEP 246. So, how would you propose I know which adapter I need, without spreading typechecks all over my bedraggled *CODE*?!?! Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
[Alex] I'm saying that if, by mistake, the programmer has NOT registered the A-C adapter (which would be easily coded and work perfectly), then thanks to transitivity, instead of a clear and simple error message leading to immediate diagnosis of the error, they'll get a subtle unnecessary degradation of information and resulting reduction in information quality. [Guido] I understand, but I would think that there are just as many examples of cases where having to register a trivial A-C adapter is much more of a pain than it's worth; especially if there are a number of A-B pairs and a number of B-C pairs, the number of additional A-C pairs needed could be bewildering. [Alex] Hm? I meant if there were multiple A's. For every Ai that has an Ai-B you would also have to register a trivial Ai-C. And if there were multiple C's (B-C1, B-C2, ...) then the number of extra adaptors to register would be the number of A's *times* the number of C's, in addition to the sum of those numbers for the atomic adaptors (Ai-B, B-Cj). But I would like to see some input from people with C++ experience. Here I am, at your service. [...] It's in the running for the coveted Alex's worst nightmare prize, Aha. This explains why you feel so strongly about it. But now, since I am still in favor of automatic combined adaptation *as a last resort*, I ask you to consider that Python is not C++, and that perhaps we can make the experience in Python better than it was in C++. Perhaps allowing more control over when automatic adaptation is acceptable? For example, inteface B (or perhaps this should be a property of the adapter for B-C?) might be marked so as to allow or disallow its consideration when looking for multi-step adaptations. We could even make the default don't consider, so only people who have to deal with the multiple A's and/or multiple C's all adaptable via the same B could save themselves some typing by turning it on. -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 12, at 19:16, Guido van Rossum wrote: ... [Alex] Hm? I meant if there were multiple A's. For every Ai that has an Ai-B you would also have to register a trivial Ai-C. And if there were multiple C's (B-C1, B-C2, ...) then the number of extra adaptors to register would be the number of A's *times* the number of C's, in addition to the sum of those numbers for the atomic adaptors (Ai-B, B-Cj). Ah, OK, I get it now, thanks. But now, since I am still in favor of automatic combined adaptation *as a last resort*, I ask you to consider that Python is not C++, and that perhaps we can make the experience in Python better than it was in C++. Perhaps allowing more control over when automatic adaptation is acceptable? Yes, that would be necessary to achieve parity with C++, which does now have the 'explicit' keyword (to state that a conversion must not be used as a step in a chain automatically constructed) -- defaults to acceptable in automatic chains for historical and backwards compatibility reasons. For example, inteface B (or perhaps this should be a property of the adapter for B-C?) might be marked so as to allow or disallow its consideration when looking for multi-step adaptations. We could even make the default don't consider, so only people who have to deal with the multiple A's and/or multiple C's all adaptable via the same B could save themselves some typing by turning it on. Yes, this idea you propose seems to me to be a very reasonable compromise: one can get the convenience of automatic chains of adaptations but only when the adaptations involved are explicitly asserted to be OK for that. I think that the property (of being OK for automatic/implicit/chained/transitive use) should definitely be one of the adaptation rather than of an interface, btw. Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 07:49 AM 1/12/05 -0800, Guido van Rossum wrote: [Phillip] As for the issue of what should and shouldn't exist in Python, it doesn't really matter; PEP 246 doesn't (and can't!) *prohibit* transitive adaptation. Really? Then isn't it underspecified? No; I just meant that: 1. With its current hooks, implementing transitivity is trivial; PyProtocols' interface objects have an __adapt__ that does the transitive lookup. So, as currently written, this is perfectly acceptable in PEP 246. 2. Given Python's overall flexibility, there's really no way to *stop* anybody from implementing it short of burying the whole thing in C and providing no way to access it from Python. And then somebody can still implement an extension module. ;) I'd think that by the time we actually implement PEP 246 in the Python core, this part of the semantics should be specified (at least the default behavior, even if there are hooks to change this). The default behavior *is* specified: it's just specified as whatever you want. :) What Alex and I are really arguing about is what should be the one obvious way to do it, and implicitly, what Python interfaces should do. Really, the whole transitivity argument is moot for PEP 246 itself; PEP 246 doesn't really care, because anybody can do whatever they want with it. It's Python's standard interface implementation that cares; should its __adapt__ be transitive, and if so, how transitive? (PEP 246's global registry could be transitive, I suppose, but it's only needed for adaptation to a concrete type, and I only ever adapt to interfaces, so I don't have any experience with what somebody might or might not want for that case.) Really, the only open proposals remaining (i.e. not yet accepted/rejected by Alex) for actually *changing* PEP 246 that I know of at this point are: 1. my suggestion for how to handle the LiskovViolation use case by returning None instead of raising a special exception 2. that classic classes be supported, since the old version of PEP 246 supported them and because it would make exceptions unadaptable otherwise. The rest of our discussion at this point is just pre-arguing a not-yet-written PEP for how Python interfaces should handle adaptation. :) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
RE: [Python-Dev] PEP 246, redux
At 09:46 AM 1/12/05 -0800, Michael Chermside wrote: This is a collection of responses to various things that don't appear to have been resolved yet: Phillip writes: if a target protocol has optional aspects, then lossy adaptation to it is okay by definition. Conversely, if the aspect is *not* optional, then lossy adaptation to it is not acceptable. I don't think there can really be a middle ground; you have to decide whether the information is required or not. I disagree. To belabor Alex's example, suppose LotsOfInfo has first, middle, and last names; PersonName has first and last, and FullName has first, middle initial and last. FullName's __doc__ specifically states that if the middle name is not available or the individual does not have a middle name, then None is acceptable. The error, IMO, is in registering an interface-to-interface adapter from PersonName to FullName; at best, it should be explicitly registered only for concrete classes that lack some way to provide a middle name. If you don't want to lose data implicitly, don't register an implicit adaptation that loses data. You're probably going to say okay, then register a LotsOfInfo-FullName converter, and I agree. But if no such converter is registered, I would rather have a TypeError then an automatic conversion which produces incorrect results. Then don't register a data-losing adapter for implicit adaptation for any possible input source; only the specific input sources that you need it for. it's difficult because intuitively an interface defines a *requirement*, so it seems logical to inherit from an interface in order to add requirements! Yes... I would fall into this trap as well until I'd been burned a few times. It's burned me more than just a few times, and I *still* sometimes make it if I'm not paying attention. It's just too easy to make the mistake. So, I'm actually open to considering dropping interface inheritance. For adapters, I think it's much harder to make this mistake because you have more time to think about whether your adapter is universal or not, and you can always err on the safe side. In truth, I believe I much more frequently implement class-to-interface adapters than interface-to-interface ones. I can always go back later and declare the adapter as interface-to-interface if I want, so there's no harm in starting them out as class-to-interface adapters. Gee... I'm understanding the problem a little better, but elegant solutions are still escaping me. My solution is to use class-to-interface adaptation for most adaptation, and interface-to-interface adaptation only when the adaptation can be considered correct by definition. It seems to work for me. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On Wed, Jan 12, 2005 at 10:16:14AM -0800, Guido van Rossum wrote: | But now, since I am still in favor of automatic combined adaptation | *as a last resort*, I ask you to consider that Python is not C++, and | that perhaps we can make the experience in Python better than it was | in C++. Perhaps allowing more control over when automatic adaptation | is acceptable? | | For example, inteface B (or perhaps this should be a property of the | adapter for B-C?) might be marked so as to allow or disallow its | consideration when looking for multi-step adaptations. We could even | make the default don't consider, so only people who have to deal | with the multiple A's and/or multiple C's all adaptable via the same B | could save themselves some typing by turning it on. How about not allowing transitive adaptation, by default, and then providing two techniques to help the user cope: - raise a AdaptIsTransitive(AdaptationError) exception when an adaptation has failed, but there exists a A-C pathway using an intermediate B - add a flag to adapt, allowTransitive, which defaults to False This way new developers don't accidently shoot their foot off, as Alex warns; however, the price for doing this sort of thing is cheap. The AdaptIsTransitive error could even explain the problem with a dynamic error message like: You've tried to adapt a LDAPName to a FirstName, but no direct translation exists. There is an indirect translation using FullName: LDAPName - FullName - FirstName. If you'd like to use this intermediate object, simply call adapt() with allowTransitive = True Clark ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 05:02 PM 1/12/05 +0100, Alex Martelli wrote: So, I think PEP 246 should specify that the step now called (e) [checking the registry] comes FIRST; then, an isinstance step [currently split between (a) and (d)], then __conform__ and __adapt__ steps [currently called (b) and (c)]. One question, and one suggestion. The question: should the registry support explicitly declaring that a particular adaptation should *not* be used, thus pre-empting later phases entirely? This would allow for the possibility of speeding lookups by caching, as well as the option to opt out of specific adaptations, which some folks seem to want. ;) The suggestion: rather than checking isinstance() in adapt(), define object.__conform__ such that it does the isinstance() check. Then, Liskov violation is simply a matter of returning 'None' from __conform__ instead of raising a special error. Checking the registry is after all very fast: make the 2-tuple (type(obj), protocol), use it to index into the registry -- period. So, it's probably not worth complicating the semantics at all just to fast path the common case. Okay, one more suggestion/idea: $ timeit -s d={}; d[1,2]=None d[1,2] 100 loops, best of 3: 1.65 usec per loop $ timeit -s d={}; d[1]={2:None} d[1][2] 100 loops, best of 3: 0.798 usec per loop This seems to suggest that using nested dictionaries could be faster under some circumstances than creating the two-tuple to do the lookup. Of course, these are trivially-sized dictionaries and this is also measuring Python bytecode speed, not what would happen in C. But it suggests that more investigation might be in order. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 12, at 20:39, Phillip J. Eby wrote: ... it's difficult because intuitively an interface defines a *requirement*, so it seems logical to inherit from an interface in order to add requirements! Yes... I would fall into this trap as well until I'd been burned a few times. It's burned me more than just a few times, and I *still* sometimes make it if I'm not paying attention. It's just too easy to make the mistake. So, I'm actually open to considering dropping interface inheritance. What about accepting Microsoft's QueryInterface precedent for this? I know that MS is a dirty word to many, but I did like much of what they did in COM, personally. The QI precedent would be: you can inherit interface from interface, but that does NOT intrinsically imply substitutability -- it just means the inheriting interface has all the methods of the one being subclassed, with the same signatures, without having to do a nasty copy-and-paste. Of course, one presumably could use NO_ADAPTER_NEEDED to easily (but explicitly: that makes a difference!) implement the common case in which the inheriting interface DOES want to assert that it's perfectly / losslessly / etc substitutable for the one being inherited. Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 09:05 PM 1/12/05 +0100, Alex Martelli wrote: On 2005 Jan 12, at 20:39, Phillip J. Eby wrote: It's burned me more than just a few times, and I *still* sometimes make it if I'm not paying attention. It's just too easy to make the mistake. So, I'm actually open to considering dropping interface inheritance. What about accepting Microsoft's QueryInterface precedent for this? I know that MS is a dirty word to many, but I did like much of what they did in COM, personally. The QI precedent would be: you can inherit interface from interface, but that does NOT intrinsically imply substitutability -- it just means the inheriting interface has all the methods of the one being subclassed, with the same signatures, without having to do a nasty copy-and-paste. Of course, one presumably could use NO_ADAPTER_NEEDED to easily (but explicitly: that makes a difference!) implement the common case in which the inheriting interface DOES want to assert that it's perfectly / losslessly / etc substitutable for the one being inherited. Well, you and I may agree to this, but we can't agree on behalf of everybody else who hasn't been bitten by this problem, I'm afraid. I checked PEAK and about 62 out of 150 interfaces inherited from anything else; it would not be a big deal to explicitly do the NO_ADAPTER_NEEDED thing, especially since PyProtocols has an 'advise' keyword that does the declaration, anyway; inheritance is just a shortcut for that declaration when you are using only one kind of interface, so the explicit way of defining NO_ADAPTER_NEEDED between two interfaces has only gotten used when mixing Zope or Twisted interfaces w/PyProtocols. Anyway, I'm at least +0 on dropping this; the reservation is just because I don't think everybody else will agree with this, and don't want to be appearing to imply that consensus between you and me implies any sort of community consensus on this point. That is, the adaptation from Alex and Phillip agree to community agrees is noisy at best! ;) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On Wed, 12 Jan 2005 10:16:14 -0800, Guido van Rossum [EMAIL PROTECTED] wrote: But now, since I am still in favor of automatic combined adaptation *as a last resort*, I ask you to consider that Python is not C++, and that perhaps we can make the experience in Python better than it was in C++. Perhaps allowing more control over when automatic adaptation is acceptable? For example, inteface B (or perhaps this should be a property of the adapter for B-C?) might be marked so as to allow or disallow its consideration when looking for multi-step adaptations. We could even make the default don't consider, so only people who have to deal with the multiple A's and/or multiple C's all adaptable via the same B could save themselves some typing by turning it on. +1. BTW, I _do_ use adaptation, including the 'lossy' one described in this scenario (where the mapping is imperfect, or incomplete). So having some way to tell the adaptation framework that a particular adapter is not suited to use in a transitive chain is a good thing IMHO. Generically speaking, anything that puts some control on the hands of the programmer - as long it does not stand in the way between him and the problem - is good. -- Carlos Ribeiro Consultoria em Projetos blog: http://rascunhosrotos.blogspot.com blog: http://pythonnotes.blogspot.com mail: [EMAIL PROTECTED] mail: [EMAIL PROTECTED] ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 12, at 20:51, Phillip J. Eby wrote: ... There's a very simple reason. If one is using only non-noisy adapters, there is absolutely no reason to ever define more than one adapter between the *same* two points. If you do, ...but there's no harm whatsoever done, either. If I have four interfaces I use regularly, A, B, C, D, and I have the need to adapt A-B, A-C, B-D, C-D, with every one of these four adaptations being the absolute best way (as you stated all interface adaptations must be), then why should that be at all a problem? Maybe sometimes someone will need to adapt A-D, fine -- again, no harm whatsoever, IF everything is as perfect as it MUST be for transitivity to apply unconditionally. Put it another way: say I have the first three of these adaptations, only, so everything is hunky-dory. Now I come upon a situation where I need C-D, fine, I add it: where's the error, if every one of the four adaptations is just perfect? I admit I can't sharply follow your gyrations about what's in what package, who wrote what, and why the fact that interfaces and adaptation (particularly transitive adaptation) are NOT widespread at all so far (are only used by early adopters, on average heads and shoulders above the average Python coder) makes it MORE important to provide an error in a case that, by the premises, cannot be an error (would it be LESS important to provide the error if everybody and their cousin were interfacing and adapting with exhuberance...?). All I can see is: 1. if an interface adapter must ABSOLUTELY be perfect, transitivity is fine, but the error makes no sense 2. if the error makes sense (or the assertion about less likely to be lossy makes any sense, etc etc), then transitivity is NOT fine -- adapters can be imperfect, and there is NO way to state that they are, one just gets an error message if one is SO DEUCEDLY LUCKY as to have created in the course of one's bumbling two shortest-paths of the same length I suspect [2] holds. But you're the one with experience, so if you stake that on [1], and the absolute best way unconditional assertion, then, fine, I guess, as per my previous message. But the combination of absolute best way _AND_ an error when somebody adds C-D is, in my opinion, self-contradictory: experience or not, I can't support asserting something and its contrary at the same time. then somebody is doing something redundant, and there is a possibility for error. In Not at all: each of the four above-listed adaptations may be needed to perform an unrelated adapt(...) operation. How can you claim that set of four adaptations is REDUNDANT, when adding a FIFTH one (a direct A-D) would make it fine again per your rules? This is the first time I've heard an implied claim that redundancy is something that can be eliminated by ADDING something, without taking anything away. Personally, I disagree with having transitivity at all, unless perhaps it be restricted to adaptations specifically and explicitly stated to be perfect and lossless; PJE claims that ALL adaptations MUST, ALWAYS, be perfect and lossless -- essentially, it seems to me, he _has_ to claim that, to defend transitivity being applied automatically, relentlessly, NON-optionally, NON-selectively (but then the idea of giving an error when two or more shortest-paths have the same length becomes dubious). No, it follows directly from the premise. If adapters are non-noisy, why do you need more than one adapter chain of equal length between two points? If you have such a condition, you I don't NEED the chain, but I may well need each step; and by the premise of absolute best way which you maintain, it must be innocuous if the separate steps I need end up producing more than one chain -- what difference can it make?! have a redundancy at the least, and more likely a programming error -- surely BOTH of those adapters are not correct, unless you have that excruciatingly-rare case I mentioned above. Each of the FOUR adapters coded can be absolutely perfect. Thus, the composite adapters which your beloved transitivity builds will also be perfect, and it will be absolutely harmless to pick one of them at random. BTW, Microsoft's COM's interfaces ONLY have the inferior kind of inheritance. You can say that interface ISub inherits from IBas: this means that ISub has all the same methods as IBas with the same signatures, plus it may have other methods; it does *NOT* mean that anything implementing ISub must also implement IBas, nor that a QueryInterface on an ISub asking for an IBas must succeed, or anything of that kind. In many years of COM practice I have NEVER found this issue to be a limitation -- it works just fine. I'm actually open to at least considering dropping interface inheritance transitivity, due to its actual problems in practice. Fewer than half of the interfaces in PEAK do any inheritance, so having to explicitly
Re: [Python-Dev] PEP 246, redux
At 09:30 PM 1/12/05 +0100, Alex Martelli wrote: On 2005 Jan 12, at 20:51, Phillip J. Eby wrote: ... There's a very simple reason. If one is using only non-noisy adapters, there is absolutely no reason to ever define more than one adapter between the *same* two points. If you do, ...but there's no harm whatsoever done, either. If I have four interfaces I use regularly, A, B, C, D, and I have the need to adapt A-B, A-C, B-D, C-D, with every one of these four adaptations being the absolute best way (as you stated all interface adaptations must be), then why should that be at all a problem? It isn't a problem, but *only* if A is an interface. If it's a concrete class, then A-B and A-C are not perfect adapters, so it *can* make a difference which one you pick, and you should be explicit. However, implementing an algorithm to ignore only interface-to-interface ambiguity is more complex than just hollering whenever *any* ambiguity is found. Also, people make mistakes and may have declared something they didn't mean to. The cost to occasionally be a bit more explicit is IMO outweighed by the benefit of catching bugs that might otherwise go unnoticed, but produce an ambiguity as a side-effect of the buggy part. It's *possible* that you'd still catch almost as many bugs if you ignored pure I-to-I diamonds, but I don't feel entirely comfortable about giving up that extra bit of protection, especially since it would make the checker *more* complex to try to *not* warn about that situation. Also, in general I'm wary of introducing non-determinism into a system's behavior. I consider keeping e.g. the first path declared or the last path declared to be a form of non-determinism because it makes the system sensitive to trivial things like the order of import statements. The current algorithm alerts you to this non-determinism. Perhaps it would be simplest for Python's interface system to issue a warning about ambiguities, but allow execution to proceed? (would it be LESS important to provide the error if everybody and their cousin were interfacing and adapting with exhuberance...?) Only in the use case where two people might legitimately create the same adapter, but neither of them can stop using their adapter in favor of the other person's, thus forcing them to work around the error. Or, in the case where lots of people try to define adapter diamonds and don't want to go to the trouble of having their program behave deterministically. :) 1. if an interface adapter must ABSOLUTELY be perfect, transitivity is fine, but the error makes no sense The error only makes no sense if we assume that the human(s) really *mean* to be ambiguous. Ambiguity suggests, however, that something *else* may be wrong. I suspect [2] holds. But you're the one with experience, so if you stake that on [1], and the absolute best way unconditional assertion, then, fine, I guess, as per my previous message. But the combination of absolute best way _AND_ an error when somebody adds C-D is, in my opinion, self-contradictory: experience or not, I can't support asserting something and its contrary at the same time. It's not contrary; it's a warning that Are you sure you want to waste time writing another way to do the same thing when there's already a perfectly valid way to do it with a comparable number of adaptation steps involved? Maybe your adapter is better-performing or less buggy in some way, but I'm just a machine so how would I know? Please tell me which of these adapters is the *really* right one to use, thanks. (Assuming that the machine is tactful enough to leave out mentioning that maybe you just made a mistake and declared the adapter between the wrong two points, you silly human you.) How can you claim that set of four adaptations is REDUNDANT, when adding a FIFTH one (a direct A-D) would make it fine again per your rules? This is the first time I've heard an implied claim that redundancy is something that can be eliminated by ADDING something, without taking anything away. PyProtocols doesn't say the situation is redundant, it says it's *ambiguous*, which implies a *possible* redundancy. I'm also saying that the ambiguity is nearly always (for me) an indicator of a *real* problem, not merely a not-so-explicit diamond. I don't NEED the chain, but I may well need each step; and by the premise of absolute best way which you maintain, it must be innocuous if the separate steps I need end up producing more than one chain -- what difference can it make?! Fair enough; however I think that in the event that the system must make such a choice, it must at *least* warn about non-deterministic behavior. Even if you are claiming perfect adaptation, that doesn't necessarily mean you are correct in your claim! Each of the FOUR adapters coded can be absolutely perfect. Thus, the composite adapters which your beloved transitivity builds will also be perfect,
Re: [Python-Dev] PEP 246, redux
On Wed, 12 Jan 2005 16:07:37 -0600, Ian Bicking [EMAIL PROTECTED] wrote: One case occurred to me with the discussion of strings and files, i.e., adapting from a string to a file. Let's say an IReadableFile, since files are too ambiguous. Consider the case where we are using a path object, like Jason Orendorff's or py.path. It seems quite reasonable and unambiguous that a string could be adapted to such a path object. It also seems quite reasonable and unambiguous that a path object could be adapted to a IReadableFile by opening the file at the given path. This strikes me as a strange use of adaptation -- I don't see how a string can act-as-a path object, or how a path object can act-as-a file. I see that you might be able to *create* a path object from-a string, or a file from-a path object, but IMHO this falls more into the category of object construction than object adaptation... Are these the sorts of things we can expect people to be doing with adaptation? Or is in really intended mainly for the act-as-a behavior that I had assumed...? Steve -- You can wordify anything if you just verb it. --- Bucky Katt, Get Fuzzy ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On Wed, Jan 12, 2005 at 04:07:37PM -0600, Ian Bicking wrote: | A two-step adaptation encodes specific intention that it seems transitive | adaption would be blind to. Exactly. Nice example Ian. To parrot your example a bit more concretely, the problem happens when you get two different adaptation paths. String - PathName - File String - StringIO - File Originally, Python may ship with the String-StringIO and StringIO-File adapters pre-loaded, and if my code was reliant upon this transitive chain, the following will work just wonderfully, def parse(file: File): ... parse(helloworld) by parsing helloworld content via a StringIO intermediate object. But then, let's say a new component pathutils registers another adapter pair: String-PathName and PathName-File This ambiguity causes a few problems: - How does one determine which adapter path to use? - If a different path is picked, what sort of subtle bugs occur? - If the default path isn't what you want, how do you specify the other path? I think Phillip's suggestion is the only resonable one here, ambiguous cases are an error; ask the user to register the adapter they need, or do a specific cast when calling parse(). | As I think these things through, I'm realizing that registered | adaptators really should be 100% accurate (i.e., no information loss, | complete substitutability), because a registered adapter that seems | pragmatically useful in one place could mess up unrelated code, since | registered adapters have global effects. I think this isn't all that useful; it's unrealistic to assume that adapters are always perfect. If transitive adaptation is even permitted, it should be unambiguous. Demanding that adaption is 100% perfect is a matter of perspective. I think String-StringIO and StringIO-File are perfectly pure. | Perhaps transitivity seems dangerous because that has the potential to | dramatically increase the global effects of those registered adapters. I'd prefer, 1. adaptation to _not_ be transitive (be explicit) 2. a simple mechanism for a user to register an explicit adaptation path from a source to a destination: adapt.path(String,PathName,File) to go from String-File, using PathName as an intermediate. 3. an error message, AdaptationError, to list all possible adaptation paths: Could not convert 'String' object to 'File' beacuse there is not a suitable adapter. Please consider an explicit conversion, or register a composite adapter with one of the following paths: adapt.path(String,PathName,File) adapt.path(String,StringIO,File) 3. raise an exception when _registering_ a 'path' which would conflict with any existing adapter: Could not complete adapt.path(String,PathName,File) since an existing direct adapter from String to Path already exists. Could not complete adapt.path(String,PathName,File) since an existing path String-StringIO-File is already registered. I'd rather have the latter error occur when importing modules rather than at run-time. This way, the exception is pinned on the correct library developer. Best, Clark ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 04:07 PM 1/12/05 -0600, Ian Bicking wrote: It also seems quite reasonable and unambiguous that a path object could be adapted to a IReadableFile by opening the file at the given path. Not if you think of adaptation as an as-a relationship, like using a screwdriver as a hammer (really an IPounderOfNails, or some such). It makes no sense to use a path as a readable file, so this particular adaptation is bogus. The problem is with the first example, where two seemingly innocuous adapters (string-path, path-IReadableFile) allow a new adaptation that could cause all sorts of problems (string-IReadableFile). Two problems with this thought: 1) path-IReadableFile is bogus 2) even if you had path-IReadableFile, you're not broken unless you extend transitivity to pass through concrete target types (which I really don't recommend) Ideally, if I had code that was looking for a file object and I wanted to accept filenames, I'd want to try to adapt to file, and if that failed I'd try to adapt to the path object and then from there to the file object. There are two reasonable ways to accomplish this. You can have code that expects an open stream -- in which case what's the harm in wrapping open() arount the value you pass if you want it to be opened? OR, you can have code that expects an openable stream, in which case you can pass it any of these: 1. an already-open stream (that then adapts to an object with a trivial 'open()' method), 2. a path object that implements openable stream 3. a string that adapts to openable stream by conversion to a path object The only thing you can't implicitly pass in that case is a string-to-be-a-StringIO; you have to explicitly make it a StringIO. In *either* case, you can have a string adapt to either a path object or to a StringIO; you just can't have both then come back to a common interface. As I think these things through, I'm realizing that registered adaptators really should be 100% accurate (i.e., no information loss, complete substitutability), because a registered adapter that seems pragmatically useful in one place could mess up unrelated code, since registered adapters have global effects. Perhaps transitivity seems dangerous because that has the potential to dramatically increase the global effects of those registered adapters. However, if you: 1) have transitivity only for interface-to-interface relationships (allowing only one class-to-interface link at the start of the path), and 2) use adaptation only for as a relationships, not to represent operations on objects you avoid these problems. For example, avoiding the one adapter you presented that's not as a, the adapter diamond becomes a triangle. The longer the discussion goes on, however, the more I realize that like the internet, transitivity depends on the continued goodwill of your neighbors, and it only takes one fool to ruin things for a lot of people. On the other hand, I also hate the idea of having to kludge workarounds like the one James Knight was doing, in order to get a simple adaptation to work. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
Phillip J. Eby wrote: At 04:07 PM 1/12/05 -0600, Ian Bicking wrote: It also seems quite reasonable and unambiguous that a path object could be adapted to a IReadableFile by opening the file at the given path. Not if you think of adaptation as an as-a relationship, like using a screwdriver as a hammer (really an IPounderOfNails, or some such). It makes no sense to use a path as a readable file, so this particular adaptation is bogus. I started to realize that in a now-aborted reply to Steven, when my defense of the path-IReadableFile adaptation started making less sense. It's *still* not intuitively incorrect to me, but there's a couple things I can think of... (a) After you adapted the path to the file, and have a side-effect of opening a file, it's unclear who is responsible for closing it. (b) The file object clearly has state the path object doesn't have, like a file position. (c) You can't go adapting the path object to a file whenever you wanted, because of those side effects. So those are some more practical reasons that it *now* seems bad to me, but that wasn't my immediate intuition, and I could have happily written out all the necessary code without countering that intuition. In fact, I've misused adaptation before (I think) though in different ways, and it those mistakes haven't particularly improved my intuition on the matter. If you can't learn from mistakes, how can you learn? One way is with principles and rules, even if they are flawed or incomplete. Perhaps avoiding adaptation diamonds is one such rule; it may not be necessarily and absolutely a bad thing that there is a diamond, but it is often enough a sign of problems elsewhere that it may be best to internalize that belief anyway. Avoiding diamonds alone isn't enough of a rule, but maybe it's a start. -- Ian Bicking / [EMAIL PROTECTED] / http://blog.ianbicking.org ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 11, at 11:01, Alex Martelli wrote: On 2005 Jan 10, at 18:59, Phillip J. Eby wrote: At 12:43 PM 1/10/05 -0500, Phillip J. Eby wrote: As a practical matter, all of the existing interface systems (Zope, PyProtocols, and even the defunct Twisted implementation) treat interface inheritance as guaranteeing substitutability for the base interface, and do so transitively. An additional data point, by the way: the Eclipse Java IDE has an adaptation system that works very much like PEP 246 does, and it appears that in a future release they intend to support automatic adapter transitivity, so as to avoid requiring each provider of an interface to provide O(n^2) adapters when writing the nth version of an interface. IOW, their current release is transitive only for interface inheritance ala Zope or Twisted; their future release will be transitive for adapter chains ala PyProtocols. This is definitely relevant prior art, so thanks for pointing it out. If interfaces change so often that 'n' can become worryingly high, this is a valid concern. In my world, though, published interfaces do NOT change as often as to require such remedies;-). ...that was a bit too flippant -- I apologize. It DOES happen that interfaces keep changing, and other situations where adapter-chain transitivity is quite handy do, absolutely!, occur, too. Reflecting on Microsoft's QI (QueryInterface), based on a very strong injunction against changing interfaces and yet mandating transitivity, points that out -- that's prior art, too, and a LOT more of it than Eclipse can accumulate any time soon, considering how long COM has been at the heart of Microsoft's components strategy, how many millions of programmers have used or abused it. Still, QI's set of constraints, amounting to a full-fledged equivalence relationship among all the adapters for a single underlying object, is, I fear, stronger than we can impose (so it may be that Eclipse is a better parallel, but I know little of it while COM is in my bones, so that's what I keep thinking of;-). So, I see transitivity as a nice thing to have _IF_ it's something that gets explicitly asserted for a certain adapter -- if the adapter has to explicitly state to the system that it isn't lossy (maybe), or isn't noisy (perhaps more useful), or something like that... some amount of reassurance about the adapter that makes it fully safe to use in such a chain. Maybe it might suffice to let an adapter which IS 'lossy' (or, more likely, one that is 'noisy') state the fact. I'm always reluctant by instinct to default to convenient but risky behavior, trusting programmers to explicitly assert otherwise when needed; but in many ways this kind of design is a part of Python and works fine (_with_ the BDFL's fine nose/instinct for making the right compromise between convenience and safety in each case, of course). I'm still pondering the don't adapt an adapter suggestion, which seems a sound one, and yet also seems to be, intrinsically, what transitivity-by-chaining does. Note that QI does not suffer from this, because it lets you get the underlying object identity (IUnknown) from any interface adapter. Maybe, just maybe, we should also consider that -- a distinguished protocol bereft of any real substance but acting as a flag for real unadapted object identity. Perhaps we could use 'object' for that, at least if the flow of logic in 'adapt' stays as in the current PEP 246 draft (i.e., __conform__ is given a chance before isinstance triggers -- so, all adapters could __conform__ to object by returning the underlying object being adapted, while other objects without such a feature in __conform__ would end up with 'adapt(x, object) is x'). Or, if object in this role turns out to be confusing, IDentity (;-) or some other specially designed protocol. If we had this ability to get at the underlying object we could at least write clearer axioms about what transitivity must mean, as well as, help out with the adapting an adapter problems. E.g., imagine: def f(x: IFoo, y: IFoo): if x is y: ... that wouldn't work if adapt(x, IFoo) returns a separate adapter each time, which is the most likely situation (think, again, of str-file adaptation by StringIO wrapping); but recovering underlying identities by adapt(x, object) is adapt(y, object) would work. I don't think that IUnknown or an equivalent, per se, can do *instead* of the need to have an adapter explicitly state it's non-noisy (or VV). Besides the need to check for object identity, which is pretty rare except when writing axioms, invariants or pre/post-conds;-), the IUnknown equivalent would perhaps be more of a conceptual/philosophical 'prop' than a practically useful feature -- while I see the ability to block unintended consequences of inheritance and transitivity (or even better, state explicitly when those consequences are wanted, even if that should be 90% of the
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 11, at 16:34, Phillip J. Eby wrote: ... Anyway, I agree that your version of the code should be used to form the reference implementation, since the purpose of the reference implementation is to show the complete required semantics. Great, one point at last on which we fully agree -- thanks Armin!-) I was waiting for BDFL feedback before editing the PEP again, but if none is forthcoming I guess at some point I'll go ahead and at least to the edits that are apparently not controversial, like this one. I'd like to have a summary of controversial points and short pro and con args, too, but I'm not unbiased enough to write it all by myself...;-) Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
RE: [Python-Dev] PEP 246, redux
Phillip: I think you must inhabit a far more perfect world than I do. You say, for instance, that: ...-1 if this introduces a performance penalty [...] just to support people who want to create deliberate Liskov violations. I personally don't think that we should pander to Liskov violators ... but in my world, people violate Liskov all the time, even in languages that attempt (unsuccessfully) to enforce it. [1] You say that: I think one should adapt primarily to interfaces, and interface-to-interface adaptation should be reserved for non-lossy, non-noisy adapters. ... but in my world, half the time I'm using adaptation to correct for the fact that someone else's poorly-written code requests some class where it should have just used an interface. You seem to inhabit a world in which transitivity of adaptation can be enforced. But in my world, people occasionally misuse adaptation because they think they know what they're doing or because they're in a big hurry and it's the most convenient tool at hand. I wish I lived in your world, but I don't. -- Michael Chermside [1] - Except for Eiffel. Eiffel seems to do a pretty good job of enforcing it. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
RE: [Python-Dev] PEP 246, redux
At 09:27 AM 1/11/05 -0800, Michael Chermside wrote: Phillip: I think you must inhabit a far more perfect world than I do. You say, for instance, that: ...-1 if this introduces a performance penalty [...] just to support people who want to create deliberate Liskov violations. I personally don't think that we should pander to Liskov violators I've since dropped both the performance objection and the objection to supporting Liskov violation; in a more recent post I've proposed an alternative algorithm for allowing it, that has a simpler implementation. You say that: I think one should adapt primarily to interfaces, and interface-to-interface adaptation should be reserved for non-lossy, non-noisy adapters. ... but in my world, half the time I'm using adaptation to correct for the fact that someone else's poorly-written code requests some class where it should have just used an interface. PEP 246 adaptation? Or are you talking about some other language? (I ask out of curiosity.) I agree that if it's possible to adapt to concrete types, people will do so. However, I think we all agree that this isn't a great idea and should still be considered bad style. That's not the same thing as saying it should be forbidden, and I haven't said it should be forbidden. You seem to inhabit a world in which transitivity of adaptation can be enforced. But in my world, people occasionally misuse adaptation because they think they know what they're doing or because they're in a big hurry and it's the most convenient tool at hand. How is this different from abuse of *any* language feature that you're then forced to work around? Are you saying we should not provide a feature because *some* people will abuse the feature? I don't understand. If you allow interface inheritance, you're just as susceptible to an invalid adaptation path, and in my experience this is more likely to bite you unintentionally, mainly because interface inheritance works differently than class inheritance (which of course is used more often). Do you want to prohibit interface inheritance, too? ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On Tue, Jan 11, 2005 at 12:54:36PM -0500, Phillip J. Eby wrote: | * Replacing LiskovViolation is possible by dropping type/isinstance | checks from adapt(), and adding an isinstance check to | object.__conform__; Liskov violators then override __conform__ in their | class to return None when asked to conform to a protocol they wish to | reject, and return super().__conform__ for all other cases. This | achieves your use case while simplifying both the implementation and the | usage. I'd rather not assume that class inheritance implies substitutability, unless the class is marked as an interface (assuming that one doesn't have interfaces). I'd like it to be explicit -- a bit of a nudge to remind a developer to verify substitutability is a good thing. In this scenerio, a LiskovViolation exception isn't needed (aside, I don't see the rationale for the exception: to prevent third party adapters?). Could we make a boilerplate __conform__ which enables class-based substitutability a well-known decorator? | * In my experience, incorrectly deriving an interface from another is the | most common source of unintended adaptation side-effects, not adapter | composition It'd be nice if interfaces had a way to specify a test-suite that could be run against a component which claims to be compliant. For example, it could provide invalid inputs and assert that the proper errors are returned, etc. Best, Clark -- Clark C. Evans Prometheus Research, LLC. http://www.prometheusresearch.com/ o office: +1.203.777.2550 ~/ , mobile: +1.203.444.0557 // (( Prometheus Research: Transforming Data Into Knowledge \\ , \/- Research Exchange Database /\- Survey Assessment Technologies ` \ - Software Tools for Researchers ~ * ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
David Ascher writes: Terminology point: I know that LiskovViolation is technically correct, but I'd really prefer it if exception names (which are sometimes all users get to see) were more informative for people w/o deep technical background. Would that be possible? I don't see how. Googling on Liskov immediately brings up clear and understandable descriptions of the principle that's being violated. I can't imagine summarizing the issue more concisely than that! What would you suggest? Including better explanations in the documentation is a must, but LiskovViolation in the exception name seems unbeatably clear and concise. -- Michael Chermside ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 01:50 PM 1/11/05 -0500, Clark C. Evans wrote: On Tue, Jan 11, 2005 at 12:54:36PM -0500, Phillip J. Eby wrote: | * Replacing LiskovViolation is possible by dropping type/isinstance | checks from adapt(), and adding an isinstance check to | object.__conform__; Liskov violators then override __conform__ in their | class to return None when asked to conform to a protocol they wish to | reject, and return super().__conform__ for all other cases. This | achieves your use case while simplifying both the implementation and the | usage. I'd rather not assume that class inheritance implies substitutability, Hm, you should take that up with Alex then, since that is what his current PEP 246 draft does. :) Actually, the earlier drafts did that too, so I'm not sure why you want to change this now. What I've actually suggested here actually allows for inheritance=substitutability as the default, but also makes it trivially changeable for any given inheritance hierarchy by overriding __conform__ at the base of that hierarchy, and without introducing a special exception class to do it. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 09:23 PM 1/11/05 +0100, Alex Martelli wrote: Is the concept of *PRAGMATICS* so deucedly HARD for all of your eggheads?! Hmm. Pot, meet kettle. :) Yes, you're ALLOWED to stuff with NULL any field that isn't explicitly specified as NOT NULL. But you should ONLY do so when the information is REALLY missing, NOT when you've lost it along the way because you've implemented adapter-chain transitivity: dropping information which you COULD have preserved with a bit more care (==without transitivity) is a violation of PRAGMATICS, of the BEST-EFFORT implication, just as it would be to drop packets once in a while in a TCP/IP stack due to some silly programming bug which was passed silently. This is again a misleading analogy. You are comparing end-to-end with point-to-point. I am saying that if you have a point-to-point connection that drops all packets of a particular kind, you should not put it into your network, unless you know that an alternate route exists that can ensure those packets get through. Otherwise, you are breaking the network. Thus, I am saying that PRAGMATICALLY, it is silly to create a cable that drops all ACK packets, for example, and then plug it into your network. And especially, it's silly to turn around that as a reason that one should only use end-to-end leased lines, because that packet forwarding business is dangerously unreliable! As far as I can tell, you are arguing that you should never use packet forwarding for communication, because somebody might have a router somewhere that drops packets. While I am arguing that if a router is known to drop packets incorrectly, the router is broken and should be removed from the network, or else bypassed via another route. And, in the cases where you have a leased line direct from point A to point B, your routers should be smart enough to use that route in place of forwarding from A to C to D to B, or whatever. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 11, at 22:08, Phillip J. Eby wrote: ... Yes, you're ALLOWED to stuff with NULL any field that isn't explicitly specified as NOT NULL. But you should ONLY do so when the information is REALLY missing, NOT when you've lost it along the way because you've implemented adapter-chain transitivity: dropping information which you COULD have preserved with a bit more care (==without transitivity) is a violation of PRAGMATICS, of the BEST-EFFORT implication, just as it would be to drop packets once in a while in a TCP/IP stack due to some silly programming bug which was passed silently. This is again a misleading analogy. You are comparing end-to-end with point-to-point. I am saying that if you have a point-to-point connection that drops all packets of a particular kind, you should not put it into your network, unless you know that an alternate route exists that can ensure those packets get through. Otherwise, you are breaking the network. But adaptation is not transmission! It's PERFECTLY acceptable for an adapter to facade: to show LESS information in the adapted object than was in the original. It's PERFECTLY acceptable for an adapter to say this piece information is not known when it's adapting an object for which that information, indeed, is not known. It's only CONJOINING the two perfectly acceptable adapters, as transitivity by adapter chain would do automatically, that you end up with a situation that is pragmatically undesirable: asserting that some piece of information is not known, when the information IS indeed available -- just not by the route automatically taken by the transitivity-system. What happened here is not that either of the adapters registered is wrong: each does its job in the best way it can. The programming error, which transitivity hides (degrading the quality of information resulting from the system -- a subtle kind of degradation that will be VERY hard to unearth), is simply that the programmer forgot to register the direct adapter. Without transitivity, the programmer's mistake emerges easily and immediately; transitivity hides the mistake. By imposing transitivity, you're essentially asserting that, if a programmer forgets to code and register an A - C direct adapter, this is never a problem, as long as A - B and B - C adapters are registered, because A - B - C will give results just as good as the direct A - C would have, so there's absolutely no reason to trouble the programmer about the trivial detail that transitivity is being used. At the same time, if I understand correctly, you're ALSO saying that if two other adapters exist, A - Z and Z - C, *THEN* it's an error, because you don't know when adapting A - C whether to go via B or via Z. Well, if you consistently believe what I state in the previous paragraph, then this is just weird: since you're implicitly asserting that any old A-?-C transitive adaptation is just as good as a direct A-C, why should you worry about there being more than one such 2-step adaptation available? Roll the dice to pick one and just proceed. Please note that in the last paragraph I'm mostly trying to reason by absurd: I do NOT believe one can sensibly assert in the general case that A-?-C is just as good as A-C, without imposing FAR stronger constraints on adaptation that we possibly can (QI gets away with it because, designed from scratch, it can and does impose such constraints, essentially that all interfaces belong to ONE single object -- no independent 3rd party adaptation, which may be a bigger loss than the constraints gain, actually). I'm willing to compromise to the extent of letting any given adaptation somehow STATE, EXPLICITLY, this adaptation is lossless and perfect, and can be used as a part of transitive chains of adaptation without any cost whatsoever. If we do that, though, the adaptation system should trust this assertion, so if there are two possibilities of equal minimal length, such as A-B-C or A-Z-C, with all the steps being declared lossless and perfect, then it SHOULD just pick one by whatever criterion, since both will be equally perfect anyway -- so maybe my reasoning by absurd wasn't totally absurd after all;-). Would this compromise be acceptable to you? Alex ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On Tue, Jan 11, 2005 at 03:30:19PM -0500, Phillip J. Eby wrote: | Clark said he didn't want to assume substitutability; I was pointing out | that he could choose to not assume that, if he wished, by implementing an | appropriate __conform__ at the base of his hierarchy. Oh, that's sufficient. If someone making a base class wants to assert that derived classes should check compliance (rather than having it automagic), then they can do this. Good enough! | I don't agree with Clark's use case, but my | proposal supports it as a possibility, and yours does not. It was a straw-man; and I admit, not a particularly compelling one. | To implement a Liskov violation with my proposal, you do exactly the same | as with your proposal, *except* that you can simply return None instead | of raising an exception, and the logic for adapt() is more | straightforward. I think I prefer just returning None rather than raising a specific exception. The semantics are different: None implies that other adaptation mechanisms (like a registry) could be tried, while LiskovException implies that processing halts and no further adaptation techniques are to be used. In this case, None is the better choice for this particular case since it would enable third-parties to register a wrapper. Overall, I think both you and Alex are now proposing essentially the same thing... no? Best, Clark -- Clark C. Evans Prometheus Research, LLC. http://www.prometheusresearch.com/ o office: +1.203.777.2550 ~/ , mobile: +1.203.444.0557 // (( Prometheus Research: Transforming Data Into Knowledge \\ , \/- Research Exchange Database /\- Survey Assessment Technologies ` \ - Software Tools for Researchers ~ * ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 06:38 PM 1/11/05 -0500, Clark C. Evans wrote: | To implement a Liskov violation with my proposal, you do exactly the same | as with your proposal, *except* that you can simply return None instead | of raising an exception, and the logic for adapt() is more | straightforward. I think I prefer just returning None rather than raising a specific exception. The semantics are different: None implies that other adaptation mechanisms (like a registry) could be tried, while LiskovException implies that processing halts and no further adaptation techniques are to be used. In this case, None is the better choice for this particular case since it would enable third-parties to register a wrapper. Overall, I think both you and Alex are now proposing essentially the same thing... no? Yes; I'm just proposing shuffling the invocation of things around a bit in order to avoid the need for an exception, and in the process increasing the number of possible customizations a bit. Not that I care about those customizations as such; I just would like to simplify the protocol. I suppose there's some educational benefit in making somebody explicitly declare that they're a Liskov violator, but it seems that if we're going to support it, it should be simple. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
RE: [Python-Dev] PEP 246, redux
Thus, my argument is that an adapter like this should never be made part of the adapter system, even if there's no transitivity. However, if you agree that such an adapter shouldn't be implicit, then it logically follows that there is no problem with allowing transitivity, except of course that people may sometimes break the rule. At some point, the PEP should be extended to include a list of best practices and anti-patterns for using adapters. I don't find issues of transitivity and implicit conversion to be immediately obvious. Also, it is not clear to me how or if existing manual adaption practices should change. For example, if I need a file-like interface to a string, I currently wrap it with StringIO. How will that change it the future? By an explicit adapt/conform pair? Or by strings knowing how to conform to file-like requests? Raymond Hettinger ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
[Python-Dev] PEP 246, redux
I'm trying to understand the relation between Guido's posts on optional static typing and PEP 245 (interfaces) and 246 (adaptation). I have a couple of questions PEP 245 proposes to introduce a fundamental distinction between type and interface. However, 245 only introduces a syntax for interfaces, and says very little about the semantics of interfaces. (Basically only that if X implements Y then implements(X, Y) will return True). The semantics of interfaces are currently only implied by PEP 246, and by Guido's posts referring to 246. Unfortunately PEP 246 explicitly refuses to decide that protocols are 245-style interfaces. Therefore, it is not clear to me how acceptance of 245 would impact on 246? Specifically, what would be the difference between: x = adapt(obj, a_245_style_interface) x = adapt(obj, a_protocol_type) and, if there is no difference, what would the use-case of interfaces be? Put another way: explicit interfaces and adaptation based typing seem to be about introducing rigor (dynamic, not static) to Python. Yet, PEP 245 and 246 seems to go out of their way to give interfaces and adaptation as little baggage as possible. So, where is the rigor going to come from? On the one hand this seems very Pythonic - introduce a new feature with as little baggage as possible, and see where it evolves from there. Let the rigor flow, not from the restrictions of the language, but from the expressive power of the language. On the other hand: why not, at least: - explore in 245 how the semantics of interfaces might introduce rigor into the language. It would be particularly illuminating to find out in what way implementing an interface differs from deriving from an ABC and in what way an interface hierarchy differs semantically from a hierarchy of ABCs - rewrite 246 under the assumption that 245 (including semantics) has been accepted I would volunteer, but, for those of you who hadn't noticed yet, I don't know what I'm talking about. Cheers, Roeland Rengelink ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
RE: [Python-Dev] PEP 246, redux
At 07:52 PM 1/11/05 -0500, Raymond Hettinger wrote: Also, it is not clear to me how or if existing manual adaption practices should change. For example, if I need a file-like interface to a string, I currently wrap it with StringIO. How will that change it the future? By an explicit adapt/conform pair? Or by strings knowing how to conform to file-like requests? The goal here is to be able to specify that a function parameter takes, e.g. a readable stream, and that you should be able to either explicitly wrap in a StringIO to satisfy this, or *possibly* that you be able to just pass a string and have it work automatically. If the latter is the case, there are a variety of possible ways it might work. str.__conform__ might recognize the readable stream interface, or the __adapt__ method of the readable stream interface could recognize 'str'. Or, Alex's new proposed global type registry might contain an entry for 'str,readableStream'. Which of these is the preferred scenario very much depends on a lot of things, like who defined the readable stream interface, and whether anybody has registered an adapter for it! PyProtocols tries to answer this question by allowing you to register adapters with interfaces, and then the interface's __adapt__ method will do the actual adaptation. Zope does something similar, at least in that it uses the interface's __adapt__ method, but that method actually uses a global registry. Neither PyProtocols nor Zope make much use of actually implementing hand-coded __conform__ or __adapt__ methods, as it's too much trouble for something that's so inherently declarative anyway, and only the creator of the object class or the interface's type have any ability to define adapters that way. Given that built-in types are often handy sources of adaptation (e.g. str-to-StringIO in your example), it isn't practical in present-day Python to add a __conform__ method to the str type! Thus, in the general case it just seems easier to use a per-interface or global registry for most normal adaptation, rather than using __conform__. However, having __conform__ exist is a nice out for implementing unusual custom requirements (like easy dynamic conformance), so I don't think it should be removed. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
[Python-Dev] PEP 246, redux
I had been promising to rewrite PEP 246 to incorporate the last several years' worth of discussions c about it, and Guido's recent stop the flames artima blog post finally pushed me to complete the work. Feedback is of course welcome, so I thought I had better repost it here, rather than relying on would-be commenters to get it from CVS... I'm also specifically CC'ing Clark, the co-author, since he wasn't involved in this rewrite and of course I owe it to him to change or clearly attribute to myself anything he doesn't like to have under his own name! Thanks, Alex PEP: 246 Title: Object Adaptation Version: $Revision: 1.6 $ Author: [EMAIL PROTECTED] (Alex Martelli), [EMAIL PROTECTED] (Clark C. Evans) Status: Draft Type: Standards Track Created: 21-Mar-2001 Python-Version: 2.5 Post-History: 29-Mar-2001, 10-Jan-2005 Abstract This proposal puts forth an extensible cooperative mechanism for the adaptation of an incoming object to a context which expects an object supporting a specific protocol (say a specific type, class, or interface). This proposal provides a built-in adapt function that, for any object X and any protocol Y, can be used to ask the Python environment for a version of X compliant with Y. Behind the scenes, the mechanism asks object X: Are you now, or do you know how to wrap yourself to provide, a supporter of protocol Y?. And, if this request fails, the function then asks protocol Y: Does object X support you, or do you know how to wrap it to obtain such a supporter? This duality is important, because protocols can be developed after objects are, or vice-versa, and this PEP lets either case be supported non-invasively with regard to the pre-existing component[s]. Lastly, if neither the object nor the protocol know about each other, the mechanism may check a registry of adapter factories, where callables able to adapt certain objects to certain protocols can be registered dynamically. This part of the proposal is optional: the same effect could be obtained by ensuring that certain kinds of protocols and/or objects can accept dynamic registration of adapter factories, for example via suitable custom metaclasses. However, this optional part allows adaptation to be made more flexible and powerful in a way that is not invasive to either protocols or other objects, thereby gaining for adaptation much the same kind of advantage that Python standard library's copy_reg module offers for serialization and persistence. This proposal does not specifically constrain what a protocol _is_, what compliance to a protocol exactly _means_, nor what precisely a wrapper is supposed to do. These omissions are intended to leave this proposal compatible with both existing categories of protocols, such as the existing system of type and classes, as well as the many concepts for interfaces as such which have been proposed or implemented for Python, such as the one in PEP 245 [1], the one in Zope3 [2], or the ones discussed in the BDFL's Artima blog in late 2004 and early 2005 [3]. However, some reflections on these subjects, intended to be suggestive and not normative, are also included. Motivation Currently there is no standardized mechanism in Python for checking if an object supports a particular protocol. Typically, existence of certain methods, particularly special methods such as __getitem__, is used as an indicator of support for a particular protocol. This technique works well for a few specific protocols blessed by the BDFL (Benevolent Dictator for Life). The same can be said for the alternative technique based on checking 'isinstance' (the built-in class basestring exists specifically to let you use 'isinstance' to check if an object is something like a string). Neither approach is easily and generally extensible to other protocols, defined by applications and third party frameworks, outside of the standard Python core. Even more important than checking if an object already supports a given protocol can be the task of obtaining a suitable adapter (wrapper or proxy) for the object, if the support is not already there. For example, a string does not support the file protocol, but you can wrap it into a StringIO instance to obtain an object which does support that protocol and gets its data from the string it wraps; that way, you can pass the string (suitably wrapped) to subsystems which require as their arguments objects that are readable as files. Unfortunately, there is currently no general, standardized way to automate this extremely important kind of adaptation by wrapping operations. Typically, today, when you pass objects to a context expecting a particular protocol, either the object knows about the context and provides its own
Re: [Python-Dev] PEP 246, redux
I had been promising to rewrite PEP 246 to incorporate the last several years' worth of discussions c about it, and Guido's recent stop the flames artima blog post finally pushed me to complete the work. Feedback is of course welcome, so I thought I had better repost it here, rather than relying on would-be commenters to get it from CVS... Thanks for doing this, Alex! I yet have to read the whole thing [will attempt do so later today] but the few snippets I caught make me feel this is a big step forward. I'm wondering if someone could do a similar thing for PEP 245, interfaces syntax? Alex hinted that it's a couple of rounds behind the developments in Zope and Twisted. I'm personally not keen on needing *two* new keywords (interface and implements) so I hope that whoever does the rewrite could add a section on the advantages and disadvantages of the 'implements' keyword (my simplistic alternative proposal is to simply include interfaces in the list of bases in the class statement; the metaclass can then sort it out). -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On 2005 Jan 10, at 18:43, Phillip J. Eby wrote: ... At 03:42 PM 1/10/05 +0100, Alex Martelli wrote: The fourth case above is subtle. A break of substitutability can occur when a subclass changes a method's signature, or restricts the domains accepted for a method's argument (co-variance on arguments types), or extends the co-domain to include return values which the base class may never produce (contra-variance on return types). While compliance based on class inheritance _should_ be automatic, this proposal allows an object to signal that it is not compliant with a base class protocol. -1 if this introduces a performance penalty to a wide range of adaptations (i.e. those using abstract base classes), just to support people who want to create deliberate Liskov violations. I personally don't think that we should pander to Liskov violators, especially since Guido seems to be saying that there will be some kind of interface objects available in future Pythons. If interfaces can ensure against Liskov violations in instances of their subclasses, then they can follow the case (a) fast path, sure. Inheriting from an interface (in Guido's current proposal, as per his Artima blog) is a serious commitment from the inheritor's part; inheriting from an ordinary type, in real-world current practice, need not be -- too many cases of assumed covariance, for example, are around in the wild, to leave NO recourse in such cases and just assume compliance. Just like any other special method in today's Python, __conform__ is meant to be taken from the object's class, not from the object itself (for all objects, except instances of classic classes as long as we must still support the latter). This enables a possible 'tp_conform' slot to be added to Python's type objects in the future, if desired. One note here: Zope and PEAK sometimes use interfaces that a function or module may implement. PyProtocols' implementation does this by adding a __conform__ object to the function's dictionary so that the function can conform to a particular signature. If and when __conform__ becomes tp_conform, this may not be necessary any more, at least for functions, because there will probably be some way for an interface to tell if the function at least conforms to the appropriate signature. But for modules this will still be an issue. I am not saying we shouldn't have a tp_conform; just suggesting that it may be appropriate for functions and modules (as well as classic classes) to have their tp_conform delegate back to self.__dict__['__conform__'] instead of a null implementation. I have not considered conformance of such objects as functions or modules; if that is important, I need to add it to the reference implementation in the PEP. I'm reluctant to just get __conform__ from the object, though; it leads to all sort of issues with a *class* conforming vs its *instances*, etc. Maybe Guido can Pronounce a little on this sub-issue... I don't see the benefit of LiskovViolation, or of doing the exact type check vs. the loose check. What is the use case for these? Is it to allow subclasses to say, Hey I'm not my superclass? It's also a bit confusing to say that if the routines raise any other exceptions they're propagated. Are you saying that LiskovViolation is *not* propagated? Indeed I am -- I thought that was very clearly expressed! LiskovViolation means to skip the loose isinstance check, but it STILL allows explicitly registered adapter factories a chance (if somebody registers such an adapter factory, presumably they've coded a suitable adapter object type to deal with some deuced Liskov violation, see...). On the other hand, if some random exception occurs in __conform__ or __adapt__, that's a bug somewhere, so the exception propagates in order to help debugging. The previous version treated TypeError specially, but I think (on the basis of just playing around a bit, admittedly) that offers no real added value and sometimes will hide bugs. If none of the first four mechanisms worked, as a last-ditch attempt, 'adapt' falls back to checking a registry of adapter factories, indexed by the protocol and the type of `obj', to meet the fifth case. Adapter factories may be dynamically registered and removed from that registry to provide third party adaptation of objects and protocols that have no knowledge of each other, in a way that is not invasive to either the object or the protocols. This should either be fleshed out to a concrete proposal, or dropped. There are many details that would need to be answered, such as whether type includes subtypes and whether it really means type or __class__. (Note that isinstance() now uses __class__, allowing proxy objects to lie about their class; the adaptation system should support this too, and both the Zope and PyProtocols interface systems and
Re: [Python-Dev] PEP 246, redux
Alex Martelli [EMAIL PROTECTED] writes: I didn't know about the let the object lie quirk in isinstance. If that quirk is indeed an intended design feature, rather than an implementation 'oops', it might perhaps be worth documenting it more clearly; I do not find that clearly spelled out in the place I'd expect it to be, namely http://docs.python.org/lib/built-in-funcs.html under 'isinstance'. Were you not at the PyPy sprint where bugs in some __getattr__ method caused infinite recursions on the isinstance's code attempting to access __class__? The isinstance code then silently eats the error, so we had (a) a massive slowdown and (b) isinstance failing in an impossible way. A clue was that if you ran the code on OS X with its silly default stack limits the code dumped core instead of going slowly insane. This is on quirk I'm not likely to forget in a hurry... Cheers, mwh -- If trees could scream, would we be so cavalier about cutting them down? We might, if they screamed all the time, for no good reason. -- Jack Handey ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On Mon, Jan 10, 2005 at 01:34:59PM -0500, Phillip J. Eby wrote: | The performance penalty I was talking about was for using an abstract | base class, in a subclass with a __conform__ method for conformance to | other protocols. In this case, __conform__ will be uselessly called | every time the object is adapted to the abstract base class. *nod* If this proposal was packaged with an interface mechanism, would this address your concern? In this scenerio, there are two cases: - Older classes will most likely not have a __conform__ method. - Newer classes will use the 'interface' mechanism. In this scenerio, there isn't a performance penalty for the usual case; and for migration purposes, a flag could be added to disable the checking. Best, Clark ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On Monday 10 January 2005 09:58 am, [EMAIL PROTECTED] wrote: Message: 3 Date: Mon, 10 Jan 2005 07:46:39 -0800 From: Guido van Rossum [EMAIL PROTECTED] Subject: Re: [Python-Dev] PEP 246, redux To: Alex Martelli [EMAIL PROTECTED] Cc: Clark C.Evans [EMAIL PROTECTED], Python Dev python-dev@python.org Message-ID: [EMAIL PROTECTED] Content-Type: text/plain; charset=US-ASCII I had been promising to rewrite PEP 246 to incorporate the last several years' worth of discussions c about it, and Guido's recent stop the flames artima blog post finally pushed me to complete the work. Feedback is of course welcome, so I thought I had better repost it here, rather than relying on would-be commenters to get it from CVS... Thanks for doing this, Alex! I yet have to read the whole thing [will attempt do so later today] but the few snippets I caught make me feel this is a big step forward. Me too! I didn't realize it the first time 246 came around how important adaptation was and how interfaces just aren't as useful without it. I'm wondering if someone could do a similar thing for PEP 245, interfaces syntax? Alex hinted that it's a couple of rounds behind the developments in Zope and Twisted. Nothing implements 245, which is just about the syntax, I intended to write another PEP describing an implementation, at the time Jim's original straw-man; which I'm glad I didn't do as it would have been a waste of time. Had I written that document, then it would be a copule of rounds behind Zope and Twisted. But as it stands now nothing need be based on 245. I'm personally not keen on needing *two* new keywords (interface and implements) so I hope that whoever does the rewrite could add a section on the advantages and disadvantages of the 'implements' keyword (my simplistic alternative proposal is to simply include interfaces in the list of bases in the class statement; the metaclass can then sort it out). I like implements, but any spelling works for me. implements strikes me as an elegant counterpart to interface and risks minimal breakage. Can we still import and say implements() for b/w compatibility and for those of us who do want an explicit statement like that? -Michel ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 04:16 PM 1/10/05 -0800, Michel Pelletier wrote: From: Guido van Rossum [EMAIL PROTECTED] Subject: Re: [Python-Dev] PEP 246, redux I'm wondering if someone could do a similar thing for PEP 245, interfaces syntax? Alex hinted that it's a couple of rounds behind the developments in Zope and Twisted. Nothing implements 245, which is just about the syntax, The comment Guido's alluding to was mine; I was referring to PEP 245's use of '__implements__', and the difference between what a class implements and an instance provides. Twisted and Zope's early implementations just looked for ob.__implements__, which leads to issues with distinguishing between what a class provides from what its instances provide. So, I was specifically saying that this aspect of PEP 245 (and Guido's basing a Python interface implementation thereon) should be re-examined in the light of current practices that avoid this issue. (I don't actually know what Zope currently does; it was changed after I had moved to using PyProtocols. But the PyProtocols test suite tests that Zope does in fact have correct behavior for instances versus classes, because it's needed to exercise the PyProtocols-Zope interop tests.) I like implements, but any spelling works for me. implements strikes me as an elegant counterpart to interface and risks minimal breakage. Can we still import and say implements() for b/w compatibility and for those of us who do want an explicit statement like that? If I understand Guido's proposal correctly, it should be possible to make a backward-compatible 'implements()' declaration function. Maybe not *easy*, but certainly possible. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
Alex Martelli [EMAIL PROTECTED] writes: PEP: 246 Title: Object Adaptation Minor nit (or not?): You could provide a pointer to the Liskov substitution principle, for those reader that aren't too familiar with that term. Besides, the text mentions three times that LiskovViolation is a subclass of AdaptionError (plus once in the ref impl section). Thomas ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
On Jan 10, 2005, at 16:38, Phillip J. Eby wrote: At 07:42 PM 1/10/05 +0100, Alex Martelli wrote: On 2005 Jan 10, at 18:43, Phillip J. Eby wrote: ... I am not saying we shouldn't have a tp_conform; just suggesting that it may be appropriate for functions and modules (as well as classic classes) to have their tp_conform delegate back to self.__dict__['__conform__'] instead of a null implementation. I have not considered conformance of such objects as functions or modules; if that is important, It's used in at least Zope and PEAK; I don't know if it's in use in Twisted. SVN trunk of Twisted (what will be 2.0) uses zope.interface. It still has the older stuff implemented as a wrapper on top of zope.interface, but I think the guideline is to just use zope.interface directly for new code dependent on Twisted 2.0. -bob ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 246, redux
At 05:42 PM 1/10/05 -0500, Bob Ippolito wrote: On Jan 10, 2005, at 16:38, Phillip J. Eby wrote: At 07:42 PM 1/10/05 +0100, Alex Martelli wrote: On 2005 Jan 10, at 18:43, Phillip J. Eby wrote: ... I am not saying we shouldn't have a tp_conform; just suggesting that it may be appropriate for functions and modules (as well as classic classes) to have their tp_conform delegate back to self.__dict__['__conform__'] instead of a null implementation. I have not considered conformance of such objects as functions or modules; if that is important, It's used in at least Zope and PEAK; I don't know if it's in use in Twisted. SVN trunk of Twisted (what will be 2.0) uses zope.interface. What I meant was, I don't know if Twisted actually *uses* interface declarations for modules and functions. It has the ability to do so, certainly. I was just saying I didn't know if the ability is actually used. PEAK uses some interfaces for functions, but I don't think I've ever used them for modules, and can think of only one place in PEAK where it would make sense to declare a module as supporting an interface. Zope policy is to use interfaces for *everything*, though, including documenting the interface provided by modules. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com