RE: Adaptation and typecasting (was Re: [Python-Dev] replacing 'global')

At 10:03 AM 10/29/03 +1100, Delaney, Timothy C (Timothy) wrote:
Yes, just like 2+2==4 and 2*2==4.
A string cannot be used as an int, although an int can be created from the string representation of an int.
I'd often like to "use a string as an integer", or use some arbitrary object as an integer. Of course, there's a perfectly valid way to express this now (i.e. 'int()'), and I think that's fine and in my code I will personally prefer to use int() to mean I want an int, because that's clearer. But, if for some reason I have code that is referencing some protocol as a *parameter*, say 'p', and I have no way to know in advance that p==int, then the most sensible thing to do is 'adapt(x,p)', rather than 'p(x)'. (Assuming 'p' is expected to be a protocol, rather than a conversion function.) Now, given that 'p' *might* be 'int' in some cases, it seems reasonable to me that adapt("23",p) should return 23 in such a case. Since 23 satisfies the desired contract (int) on behalf of "23", this seems to be a correct adaptation. For a protocol p that has immutability as part of its contract, adapt(x,p) is well within its rights to return an object that is a "copy" of x in some sense. The immutability requirement means that the "adapted" value can never change, so really it's a *requirement* that the "adaptation" be a snapshot.
I agree 100% -- for a protocol whose contract doesn't require immutability, the way 'int' does. I think now that I understand, however, why you and Alex think I'm saying something different than I've been saying. To both of you, "typecasting" means "convert to a different type" at an *implementation* level (as it is in other languages), and I mean at a *logical* level. Thus, to me, "I would like to use X as a Y" includes whatever contracts Y supplies *as applied to X*. Not, "give me an instance of Y that's a copy of X". It just so happens, however, that for a protocol whose contract includes immutability, these two concepts overlap, just as multiplication and addition overlap for the case of 2+2==2*2. So, IMO, for immutable types such as tuple, str, int, and float, I believe that it's reasonable for adapt(x,p)==p(x) iff x is not an instance of p already, and does not have a __conform__ method that overrides this interpretation. That such a default interpretation is redundant with p(x), I also agree. However, for code that uses protocols dynamically, that redundancy would eliminate the need to make a dummy protocol (e.g. 'IInteger') to use in place of 'int'. OTOH, if Guido decides that Python's eventual interface objects shouldn't be types, then there will be an IInteger anyway, and the point becomes moot. Anyway, I can only understand Alex's objection to such adaptation if he is saying that there is no such thing as adapting to an immutable protocol! In that case, there could never exist such a thing as IInteger, because you could never adapt anything to it that wasn't already an IInteger. Somehow, this seems wrong to me.

"Phillip J. Eby" <pje@telecommunity.com>:
I don't think that's right -- this should only apply if the original object x is immutable. Otherwise, changes to x should be reflected in the view of it provided by p -- even if p itself provides no operations for mutation. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

At 02:37 PM 10/29/03 +1300, Greg Ewing wrote:
There's a difference between an interface which provides no methods for mutation, and an interface that *requires* immutability. Part of the concept of an 'int' or 'tuple' is that it is a *value* and therefore unchanging. Thus, one might say that IInteger or ITuple conceptually derive from IValueObject. However, that doesn't mean we can't say that adapt([1,2,3],tuple) should fail, and I'm certainly open to the possibility of such an interpretation, if it's decreed that supporting 'tuple' means guaranteeing that the adaptee doesn't change state, not merely the adapted form. It seems there are three levels of "immutable" one may have in an interface/protocol: 1. No mutator methods, but no requirements regarding stability of state 2. Immutability is required of the adapted form (snapshot) 3. Immutability is required of the adaptee I have made plenty of use of cases 1 and 2, but never 3. I'm having a hard time thinking of a use case for it, so that's probably why it hasn't occurred to me before now. Looking at this list, I now understand at least one of Alex's points better: he (and I think you) are assuming that an immutable target protocol means case 3. That has been baffling the heck out of me, because I have not yet encountered a use case for 3. On the other hand, it's possible that I *have* seen use case 3, and mistaken it for use case 2, simply because all the types I wrote adapters for were immutable. Given all this, I think I'm okay with saying that adapting from a mutable object to an immutable interface (e.g list->tuple) is an improper use of adaptation. Presumably this also means StringIO->str adaptation would be invalid as well. But, int<->str and other such immutable-to-immutable conversions seem well within the purview of adaptation.

"Phillip J. Eby" <pje@telecommunity.com>:
Expecting such an adaptation to somehow make the underlying list unchangeable by any means would be unreasonable, I think. I can't see any way of enforcing that other than by making a copy, which goes agains the spirit of adaptation. There still might be uses for it, though, without any unchangeability guarantee, such as passing it to something that requires a tuple and not just a sequence, but not wanting the overhead of making a copy. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

On Wednesday 29 October 2003 03:56, Greg Ewing wrote:
There are uses for both permanent (via copy) and temporary freezing. For example: checking if a list is an element of a set will need only temporary freezing -- just enough to let the list supply a hash value. Adding the list to the set will need a frozen copy. Right now, the sets.py code tries for both kinds of adaptation via special methods -- __as_immutable__ and __as_temporarily_immutable__ -- but that's just the usual ad hoc approach. If we had adaptation I'd want both of these to go via protocol adaptation, just because that will allow adaptation strategies to be supplied by protocol, object type AND third parties -- practicality beats purity, i.e., even though you are puristically right that adaptation normally shouldn't copy, I find this one a compelling very practical use case. Adaptation altering the object itself, as in "setting a flag in the list to make it permanently reject any further changes", WOULD on the other hand be a very bad thing -- one could never safely try adaptation any longer if one had to fear such permanent effects on the object being adapted. Alex

"Phillip J. Eby" <pje@telecommunity.com>:
I don't think that's right -- this should only apply if the original object x is immutable. Otherwise, changes to x should be reflected in the view of it provided by p -- even if p itself provides no operations for mutation. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

At 02:37 PM 10/29/03 +1300, Greg Ewing wrote:
There's a difference between an interface which provides no methods for mutation, and an interface that *requires* immutability. Part of the concept of an 'int' or 'tuple' is that it is a *value* and therefore unchanging. Thus, one might say that IInteger or ITuple conceptually derive from IValueObject. However, that doesn't mean we can't say that adapt([1,2,3],tuple) should fail, and I'm certainly open to the possibility of such an interpretation, if it's decreed that supporting 'tuple' means guaranteeing that the adaptee doesn't change state, not merely the adapted form. It seems there are three levels of "immutable" one may have in an interface/protocol: 1. No mutator methods, but no requirements regarding stability of state 2. Immutability is required of the adapted form (snapshot) 3. Immutability is required of the adaptee I have made plenty of use of cases 1 and 2, but never 3. I'm having a hard time thinking of a use case for it, so that's probably why it hasn't occurred to me before now. Looking at this list, I now understand at least one of Alex's points better: he (and I think you) are assuming that an immutable target protocol means case 3. That has been baffling the heck out of me, because I have not yet encountered a use case for 3. On the other hand, it's possible that I *have* seen use case 3, and mistaken it for use case 2, simply because all the types I wrote adapters for were immutable. Given all this, I think I'm okay with saying that adapting from a mutable object to an immutable interface (e.g list->tuple) is an improper use of adaptation. Presumably this also means StringIO->str adaptation would be invalid as well. But, int<->str and other such immutable-to-immutable conversions seem well within the purview of adaptation.

"Phillip J. Eby" <pje@telecommunity.com>:
Expecting such an adaptation to somehow make the underlying list unchangeable by any means would be unreasonable, I think. I can't see any way of enforcing that other than by making a copy, which goes agains the spirit of adaptation. There still might be uses for it, though, without any unchangeability guarantee, such as passing it to something that requires a tuple and not just a sequence, but not wanting the overhead of making a copy. Greg Ewing, Computer Science Dept, +--------------------------------------+ University of Canterbury, | A citizen of NewZealandCorp, a | Christchurch, New Zealand | wholly-owned subsidiary of USA Inc. | greg@cosc.canterbury.ac.nz +--------------------------------------+

On Wednesday 29 October 2003 03:56, Greg Ewing wrote:
There are uses for both permanent (via copy) and temporary freezing. For example: checking if a list is an element of a set will need only temporary freezing -- just enough to let the list supply a hash value. Adding the list to the set will need a frozen copy. Right now, the sets.py code tries for both kinds of adaptation via special methods -- __as_immutable__ and __as_temporarily_immutable__ -- but that's just the usual ad hoc approach. If we had adaptation I'd want both of these to go via protocol adaptation, just because that will allow adaptation strategies to be supplied by protocol, object type AND third parties -- practicality beats purity, i.e., even though you are puristically right that adaptation normally shouldn't copy, I find this one a compelling very practical use case. Adaptation altering the object itself, as in "setting a flag in the list to make it permanently reject any further changes", WOULD on the other hand be a very bad thing -- one could never safely try adaptation any longer if one had to fear such permanent effects on the object being adapted. Alex
participants (3)
-
Alex Martelli
-
Greg Ewing
-
Phillip J. Eby