[Python-checkins] CVS: python/nondist/peps pep-0208.txt,1.3,1.4

Neil Schemenauer python-dev@python.org
Mon, 4 Dec 2000 19:17:11 -0800


Update of /cvsroot/python/python/nondist/peps
In directory slayer.i.sourceforge.net:/tmp/cvs-serv28644

Modified Files:
	pep-0208.txt 
Log Message:
Add some meat to the PEP (mostly from Marc-Andre's web page).


Index: pep-0208.txt
===================================================================
RCS file: /cvsroot/python/python/nondist/peps/pep-0208.txt,v
retrieving revision 1.3
retrieving revision 1.4
diff -C2 -r1.3 -r1.4
*** pep-0208.txt	2000/11/28 22:23:25	1.3
--- pep-0208.txt	2000/12/05 03:17:08	1.4
***************
*** 2,8 ****
  Title: Reworking the Coercion Model
  Version: $Revision$
! Author: davida@activestate.com (David Ascher), pep@zadka.site.co.il (Moshe Zadka) 
  Python-Version: 2.1
! Status: Incomplete
  
  
--- 2,211 ----
  Title: Reworking the Coercion Model
  Version: $Revision$
! Author: Neil Schemenauer <nas@arctrix.com>
! Status: Draft
! Type: Standards Track
! Created: 04-Dec-2000
! Post-History:
  Python-Version: 2.1
! 
! 
! Abstract
! 
!     Many Python types implement numeric operations.  When the arguments of
!     a numeric operation are of different types, the interpreter tries to
!     coerce the arguments into a common type.  The numeric operation is
!     then performed using this common type.  This PEP proposes a new type
!     flag to indicate that arguments to a type's numeric operations should
!     not be coerced.  Operations that do not support the supplied types
!     indicate it by returning a new singleton object.  Types which do not
!     set the type flag are handled in a backwards compatible manner.
!     Allowing operations handle different types is often simpler, more
!     flexible, and faster then having the interpreter do coercion.  
! 
! 
! Rational
! 
!     When implementing numeric or other related operations, it is often
!     desirable to provide not only operations between operands of one type
!     only, e.g. integer + integer, but to generalize the idea behind the
!     operation to other type combinations as well, e.g. integer + float. 
! 
!     A common approach to this mixed type situation is to provide a method
!     of "lifting" the operands to a common type (coercion) and then use
!     that type's operand method as execution mechanism. Yet, this strategy
!     has a few drawbacks: 
! 
!         * the "lifting" process creates at least one new (temporary)
!           operand object, 
! 
!         * since the coercion method is not being told about the operation
!           that is to follow, it is not possible to implement operation
!           specific coercion of types, 
! 
!         * there is no elegant way to solve situations were a common type
!           is not at hand, and 
! 
!         * the coercion method will always have to be called prior to the
!           operation's method itself. 
! 
!     A fix for this situation is obviously needed, since these drawbacks
!     make implementations of types needing these features very cumbersome,
!     if not impossible. As an example, have a look at the DateTime and
!     DateTimeDelta[1] types, the first being absolute, the second
!     relative.  You can always add a relative value to an absolute one,
!     giving a new absolute value. Yet, there is no common type which the
!     existing coercion mechanism could use to implement that operation.
! 
!     Currently, PyInstance types are treated specially by the interpreter
!     in that their numeric methods are passed arguments of different types.
!     Removing this special case simplifies the interpreter and allows other
!     types to implement numeric methods that behave like instance types.
!     This is especially useful for extension types like ExtensionClass.
! 
! 
! Specification
! 
!     Instead of using a central coercion method, the process of handling
!     different operand types is simply left to the operation. If the
!     operation finds that it cannot handle the given operand type
!     combination, it may return a special singleton as indicator. 
! 
!     Note that "numbers" (anything that implements the number protocol, or
!     part of it) written in Python already use the first part of this
!     strategy - it is the C level API that we focus on here.
! 
!     To maintain nearly 100% backward compatibility we have to be very
!     careful to make numbers that don't know anything about the new
!     strategy (old style numbers) work just as well as those that expect
!     the new scheme (new style numbers). Furthermore, binary compatibility
!     is a must, meaning that the interpreter may only access and use new
!     style operations if the number indicates the availability of these. 
! 
!     A new style number is considered by the interpreter as such if and
!     only it it sets the type flag Py_TPFLAGS_NEWSTYLENUMBER.  The main
!     difference between an old style number and a new style one is that the
!     numeric slot functions can no longer assume to be passed arguments of
!     identical type. New style slots must check all arguments for proper
!     type and implement the necessary conversions themselves. This may seem
!     to cause more work on the behalf of the type implementor, but is in
!     fact no more difficult than writing the same kind of routines for an
!     old style coercion slot. 
! 
!     If a new style slot finds that it cannot handle the passed argument
!     type combination, it may return a new reference of the special
!     singleton Py_NotImplemented to the caller. This will cause the caller
!     to try the other operands operation slots until it finds a slot that
!     does implement the operation for the specific type combination. If
!     none of the possible slots succeed, it raises a TypeError. 
! 
!     To make the implementation easy to understand (the whole topic is
!     esoteric enough), a new layer in the handling of numeric operations is
!     introduced. This layer takes care of all the different cases that need
!     to be taken into account when dealing with all the possible
!     combinations of old and new style numbers. It is implemented by the
!     two functions _PyNumber_BinaryOperation() and
!     _PyNumber_TernaryOperation(), which both are internal functions that
!     only the functions in Objects/abstract.c have access to. The numeric
!     API (PyNumber_*) is easy to adapt to this new layer. 
! 
!     As a side-effect all numeric slots can be NULL-checked (this has to be
!     done anyway, so the added feature comes at no extra cost).
! 
!     
!     The scheme used by the layer to execute a binary operation is as
!     follows: 
! 
!       v        | w          | Action taken
!       ---------+------------+----------------------------------
!       new      | new        | v.op(v,w), w.op(v,w)
!       new      | old        | v.op(v,w), coerce(v,w), v.op(v,w)
!       old      | new        | w.op(v,w), coerce(v,w), v.op(v,w)
!       old      | old        | coerce(v,w), v.op(v,w)
! 
!     The indicated action sequence is executed from left to right until
!     either the operation succeeds and a valid result (!=
!     Py_NotImplemented) is returned or an exception is raised. Exceptions
!     are returned to the calling function as-is. If a slot returns
!     Py_NotImplemented, the next item in the sequence is executed. 
! 
!     Note that coerce(v,w) will use the old style nb_coerce slot methods
!     via a call to PyNumber_Coerce(). 
! 
!     Ternary operations have a few more cases to handle: 
!        
!       v   | w   | z   | Action taken
!       ----+-----+-----+------------------------------------
!       new | new | new | v.op(v,w,z), w.op(v,w,z), z.op(v,w,z)
!       new | old | new | v.op(v,w,z), z.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
!       old | new | new | w.op(v,w,z), z.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
!       old | old | new | z.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
!       new | new | old | v.op(v,w,z), w.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
!       new | old | old | v.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
!       old | new | old | w.op(v,w,z), coerce(v,w,z), v.op(v,w,z)
!       old | old | old | coerce(v,w,z), v.op(v,w,z)
! 
!     The same notes as above, except that coerce(v,w,z) actually does: 
! 
!         if z != Py_None: 
!             coerce(v,w), coerce(v,z), coerce(w,z) 
!         else: 
!             # treat z as absent variable 
!             coerce(v,w) 
! 
!     
!     The current implementation uses this scheme already (there's only one
!     ternary slot: nb_pow(a,b,c)). 
! 
!     Note that the numeric protocol is also used for some other related
!     tasks, e.g. sequence concatenation.  These can also benefit from the
!     new mechanism by implementing right-hand operations for type
!     combinations that would otherwise fail to work. As an example, take
!     string concatenation: currently you can only do string + string. With
!     the new mechanism, a new string-like type could implement new_type +
!     string and string + new_type, even though strings don't know anything
!     about new_type. 
! 
!     Since comparisons also rely on coercion (every time you compare an
!     integer to a float, the integer is first converted to float and then
!     compared...), a new slot to handle numeric comparisons is needed: 
! 
!         PyObject *nb_cmp(PyObject *v, PyObject *w) 
! 
!     This slot should compare the two objects and return an integer object
!     stating the result. Currently, this result integer may only be -1, 0,
!     1. If the slot cannot handle the type combination, it may return a
!     reference to Py_NotImplemented.   Note that this slot is still in flux
!     since it should take into account rich comparisons (ie. PEP 207).
! 
!     Numeric comparisons are handled by a new numeric protocol API: 
! 
!         PyObject *PyNumber_Compare(PyObject *v, PyObject *w) 
! 
!     This function compare the two objects as "numbers" and return an
!     integer object stating the result. Currently, this result integer may
!     only be -1, 0, 1. In case the operation cannot be handled by the given
!     objects, a TypeError is raised. 
! 
!     The PyObject_Compare() API needs to adjusted accordingly to make use
!     of this new API. 
! 
!     Other changes include adapting some of the built-in functions (e.g.
!     cmp()) to use this API as well. Also, PyNumber_CoerceEx() will need to
!     check for new style numbers before calling the nb_coerce slot. New
!     style numbers don't provide a coercion slot and thus cannot be
!     explicitly coerced. 
! 
! 
! Reference Implementation
! 
!     A preliminary patch for the CVS version of Python is available through
!     the Source Forge patch manager[2].
! 
! 
! References
! 
!     [1] http://www.lemburg.com/files/python/mxDateTime.html
!     [2] http://sourceforge.net/patch/download.php?id=102652
!