[Python-3000] iostack and Oh Oh (original) (raw)
Tim Hochberg tim.hochberg at ieee.org
Thu Dec 7 21🔞05 CET 2006
- Previous message: [Python-3000] iostack and Oh Oh
- Next message: [Python-3000] iostack and Oh Oh
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Talin wrote:
Phillip J. Eby wrote:
At 09:59 AM 12/5/2006 -0600, Guido van Rossum wrote:
My point is that an interface can document (at least in English) a "contract" about the invariants between operations. While I'm not into enforcing or verifying such contracts, I'm very interested in documenting them. For example, something that has "mapping" behavior has a very different relationship between x[y] and "y in x" than something that has "sequence" behavior. I assumed this didn't need answering. If you're using the interface solely for documentation, then a namespace-oriented interface suffices to provide it. I'm guessing that Guido's use of the word 'document' means something more than just conveying information to a human reader. From what I can tell, the argument boils down to this: You are saying that a class is merely the sum of its attributes and methods, and Guido is saying that it's not.
That's not my interpretation. Let me throw out my spin on my understanding of Philip's position.
Here goes: to the extent that the semantics aren't clear from the methods it is sufficient, and preferable, to attach the semantic information to the methods rather their associated class.
For example: You cannot deduce by examining the methods of a list that the keys are required to be consecutive integers starting from 0. Lists have getitem, but so do maps; They support len(), but so do maps; and so on.
No, but if you tag the methods of the class then it appears that this works.
Thus, lists embody a particular behavior contract (called 'concept' in C++ template land). (The word 'interface' and 'ability' has been used in this context, but that's somewhat misleading. [1] [2]) The concept is supported by the methods of the class, but is only incompletely derivable from them. I think what Guido is looking for is a way to signal that a class obeys a given behavioral contract, and a way to inspect the object, both at runtime and to a human reader, and discover what contracts are supported by the class.
Let me throw out an examples of how this could look.
@implements(sequence.getitem, sequence.len) class MySequence: #....
@implements(mapping.getitem, mapping.keys, mapping.len) class MyMapping: #....
#...
supports reads better than has_ability to me in this context.
if supports(someobject, mapping.getitem): # do something else: # do something else
Assuming that class decorators make it in, I believe that this could be implemented in terms of generic functions or a simple registry with no additional syntactic support.
On the plus side, this operates at the same level of granularity as duck typing and would fit more naturally into the existing duck typing scheme IMO. Also, it avoids having to muck with bases when retrofitting classes to support a [concept/interface/ability], which gives me the heebie jeebies.
Let's move on to Guido's set example:
[Guido writes]
Now I'd like to introduce a standard set implementation. It implements all those operations. But I'd like it to automatically accept operands of other set implementations, as long as they claim the relevant ability. For example, in Python 2.4, the built-in set implementation's and is defined roughly like this (ignoring some optimizations, like that it's written in C :-):
# This a method of class 'set', a concrete set implementation def and(self, other): if not isinstance(other, baseset): return NotImplemented result = set() for elem in self: if elem in other: result.add(elem) return result
This could be written:
def and(self, other): if not supports(other, set.contains): returns NotImplemented result = set() for elem in self: if elem in other: result.add(elem) return result
Where the other class has been marked as implementing set.contains. For example:
@implements(set.contains) class MySet: #....
But I'd like to be able to write it like this instead (still inside the 'set' class):
def and(self, other: MinimalSet): result = set() for elem in self: if elem in other: result.add(elem) return result
There are several ways one could translate the above. If we let a tuple of these markers denote the union of all those types, one way would be to simply define:
MinimalSet = (set.contains,)
And then the above definition could remain unchanged. Similarly:
IterableSet = (set.contains,set.iter) StandardSet = (set.contains,set.iter,set.and,...)
This gives you the ability to express the three main types of sets, but I could also indicate, for example, that I have an IterableSet that also supports '&' using (set.contains,set.iter,set.and). In my experience this type of partial implementation is pretty common: since I don't always need all of the pieces, I just implement those parts that I do. If I can only spell three types of set, I'm forced to do extra work to implement the other methods or lie about what the class supports, neither of which is ideal.
-tim
- Previous message: [Python-3000] iostack and Oh Oh
- Next message: [Python-3000] iostack and Oh Oh
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]