[Types-sig] QueryProtocol
Clark C. Evans
cce@clarkevans.com
Tue, 20 Mar 2001 18:56:11 -0500 (EST)
On Mon, 19 Mar 2001, Paul Prescod wrote:
> QueryProtocol is an idea deserving of a PEP. I think it is interesting
> but I would like to see it commonly used as a convention in Python
> programming before it became a part of the language.
Ok. I hope this is adequate. ;) Clark
---------- Forwarded message ----------
Date: Tue, 20 Mar 2001 18:43:48 -0500 (EST)
From: Clark C. Evans <cce@clarkevans.com>
To: python-list@python.org
Newsgroups: comp.lang.python
Subject: Yet Another PEP: Query Protocol Interface or __query__
Please have a look at the PEP below, it is intended to be a
light-weight solution to "typing" based on programmer intent.
Regards,
Clark Evans
PEP: XXX
Title: Query Interface Protocol
Version: $Revision$
Author: Clark Evans
Python-Version: 2.2
Status: Active
Type: Standards Track
Created: 21-Mar-2001
Summary
This paper asserts that "interface typing" can be carved
into two separable concerns, that of (a) protocol, which is
all about behavior/expectations, and that of (b) signature
which is about method existence and argument type checking.
This proposal puts forth a declarative method for interface
protocol discovery that could be orthogonal and complementary
to a more signature based approach as being developed by the
types special interest group.
Context
Python is a very dynamic language with powerful introspection
capabilities. However, it has yet to formalize an interface
or abstract type checking mechanism. A consensus for a user
defined type system may be far off into the future.
Currently, existence of particular methods, particularly those
that are built-in such as __getitem__, is used as an indicator
of support for a particular interface. This method may work
for interfaces blessed by GvR, such as the new enumerator interface
being proposed and identified by a new built-in __iter__.
However, this current method does not admit an infallible way
to identify interfaces lacking a built-in method.
Motivation
In the recent type special interest group discussion [1], there
were two complementary quotes which motivated this proposal:
"The deep(er) part is whether the object passed in thinks of
itself as implementing the Foo interface. This means that
its author has (presumably) spent at least a little time
about the invariants that a Foo should obey." - GvR [2]
and
"There is no concept of asking an object which interface it
implements. There is no "the" interface it implements. It's
not even a set of interfaces, because the object doesn't
know them in advance. Interfaces can be defined after objects
conforming to them are created." -- Marcin Kowalczyk [3]
The first quote focuses on the intent of a class, including
not only the existence of particular methods, but more
importantly the call sequence, behavior, and other invariants.
Where the second quote focuses on the type signature of the
class. These quotes motivate a distinction between interface
as a "declarative, I am a such-and-such" construct, as opposed
to a "descriptive, It looks like a such-and-such" mechanism.
Furthermore, it is clear that both aspects of interface are
important, if not completely orthogonal and complementary.
For purposes of this proposal, the word "protocol" is aligned
with Guido's deep issue, and "signature" with Marcin's vision
of a type system. In this way, we clearly demarcate the two
attitudes of the word "interface" in a type system.
Clearly, detailing an exhaustive method of interface
signatures is very difficult problem, especially for
a dynamic language like Python where the interface
signature for a class may change over time. However, a
simple declarative mechanism to inquire which interface
protocols an object supports at a given time is relatively
straight forward and could bring immediate benefit.
Details
A new built-in method, __query__ is proposed. This method has
a single argument, an interface protocol identifier, and either
returns an object supporting the given protocol, or throws an
unknown exception.
An interface protocol identifier is a lower-case string having
a reverse DNS host name string: TLD '.' DOMAIN ['.' NAME ]*
Where TLD is a top level domain such as 'com' or 'uk.co'. DOMAIN
is a registered name in the given TLD. And one or more optional
NAME separated by a period. NAME is a sequence of one or more
alphabetic characters including the dash '-' character. See the
relevant ITEF specifications for specific details.
Note that all of the protocols above have at least one period.
In the future it may be prudent to introduce one or more "blessed"
interface protocol identifiers which do not have a period. Potential
examples include "enumerator", "sequence", "map", "string", etc.
In addition, a built-in query method could be introduced that
calls the __query__ method on a given object.
Example Usage
>>> class EggsOnly:
def eggs(self,str): print "eggs!" + str
def __query__(self,protocol):
if protocol == "org.python.example.eggs": return self
raise "unknown: " + protocol
>>> class EggsAndHam:
def ham(self,str): print "ham!"+str
def __query__(self,protocol):
if protocol == "org.python.example.ham": return self
if protocol == "org.python.example.eggs":
return EggsOnly()
raise "unknown" + protocol
>>> x = EggsOnly()
>>> x.eggs("hello")
eggs!hello
>>> x.__query__("org.python.example.eggs").eggs("hello")
eggs!hello
>>> z = EggsAndHam()
>>> z.ham("hi")
ham!hi
>>> z.__query__("org.python.example.ham").ham("hi")
ham!hi
>>> z.__query__("org.python.example.eggs").eggs("hi")
eggs!hi
>>> y = x.__query__("org.python.bad")
Traceback (innermost last):
File "<interactive input>", line 1, in ?
File "<interactive input>", line 5, in __query__
unknown: org.python.bad
Relationships:
The iterator special interest group is proposing a new built-in
called "__iter__", which could be replaced with __query__ and
a blessed interface protocol identifier of "enumerator".
Therefore calls to obj.__iter__() could be replaced with
obj.__query__("enumerator") with no semantic difference.
Although this proposal may sounds similar to Microsoft's
QueryInterface, it differs by a number of aspects. First,
there is not a special "IUnknown" interface which can be used
for object identity, although this could be proposed as one
of those "special" blessed interface protocol identifiers.
Second, with QueryInterface, once an object supports a particular
interface it must always there after support this interface;
this proposal makes no such guarantee, although this may be
added at a later time. Third, implementations of Microsoft's
QueryInterface must support a kind of equivalence relation.
By reflexive they mean the querying an interface for itself
must always succeed. By symmetrical they mean that if one
can successfully query an interface IA for a second interface
IB, then one must also be able to successfully query the
interface IB for IA. And finally, by transitive they mean if
one can successfully query IA for IB and one can successfully
query IB for IC, then one must be able to successfully query
IA for IC. Ability to support this type of equivalence relation
should be encouraged, but may not be possible. Further research
on this topic (by someone familiar with Microsoft COM) would be
helpful in further determining how compatible this proposal is.
Backwards Compatibility
There should be no problem with backwards compatibility.
Indeed this proposal, save an built-in query() function,
could be tested without changes to the interpreter.
Future Compatibility
It appears that this proposal could be implemented orthogonal
to the protocol checking system being constructed [4] by Paul
Prescod and company. It may not be all that compatible with
the more ambitious signature declaration and checking approach[5]
taken by Michel Pelletier. This requires further investigation.
Questions and Answers
Q: This is just a type-coercion proposal.
A: No. Certainly it could be used for type-coercion, such
coercion would be explicit via __query__ or query function.
Of course, if this was used for iterator interface, then the
for construct may do an implicit __query__("enumerator") but
this would be an exception rather than the rule.
Q: Why did the author write this PEP?
A: He wanted a simple proposal that covered the "deep part" of
interfaces without getting tied up in signature woes. Also, it
was clear that __iter__ proposal put forth is just an example
of this type of interface. Further, the author is doing XML
based client server work, and wants to write generic tree based
algorithms that work on particular interfaces and would
like these algorithms to be used by anyone willing to make
an "adapter" having the interface required by the algorithm.
Q: Why not call this __queryinterface__ ?
A: Too close to Microsoft's QueryInterface, especially given the
semantic differences which may not be reconcilable.
Q: Is this in opposition to the type special interest group?
A: No. It is meant as a simple, need based solution that could
easily complement the efforts by that group.
Copyright
This document has been placed in the public domain.
References and Footnotes
[1] http://www.zope.org/Members/michel/types-sig/TreasureTrove
[2] http://mail.python.org/pipermail/types-sig/2001-March/001105.html
[3] http://mail.python.org/pipermail/types-sig/2001-March/001206.html
[4] http://mail.python.org/pipermail/types-sig/2001-March/001223.html