[Pythoncheckins] peps: Update to PEP 465  propose to keep @ at the same level as *.
guido.van.rossum
pythoncheckins at python.org
Mon Apr 7 01:05:18 CEST 2014
http://hg.python.org/peps/rev/e948cc3f7c8a
changeset: 5453:e948cc3f7c8a
user: Guido van Rossum <guido at dropbox.com>
date: Sun Apr 06 16:05:38 2014 0700
summary:
Update to PEP 465  propose to keep @ at the same level as *.
files:
pep0465.txt  151 +++++++++++++++++++++++++++
1 files changed, 106 insertions(+), 45 deletions()
diff git a/pep0465.txt b/pep0465.txt
 a/pep0465.txt
+++ b/pep0465.txt
@@ 27,7 +27,7 @@
======= ========================= ===============================
Op Precedence/associativity Methods
======= ========================= ===============================
``@`` *To be determined* ``__matmul__``, ``__rmatmul__``
+``@`` Same as ``*`` ``__matmul__``, ``__rmatmul__``
``@=`` n/a ``__imatmul__``
======= ========================= ===============================
@@ 761,11 +761,6 @@
* sympy
* sage
If you know of any actively maintained Python libraries which provide
an interface for working with numerical arrays or matrices, and which
are not listed above, then please let the PEP author know:
njs at pobox.com

Implementation details
======================
@@ 833,12 +828,12 @@
Finally, there is the option of using multicharacter tokens. Some
options:
* Matlab uses a ``.*`` operator. Aside from being visually confusable
 with ``*``, this would be a terrible choice for us because in
 Matlab, ``*`` means matrix multiplication and ``.*`` means
 elementwise multiplication, so using ``.*`` for matrix
 multiplication would make us exactly backwards from what Matlab
 users expect.
+* Matlab and Julia use a ``.*`` operator. Aside from being visually
+ confusable with ``*``, this would be a terrible choice for us
+ because in Matlab and Julia, ``*`` means matrix multiplication and
+ ``.*`` means elementwise multiplication, so using ``.*`` for matrix
+ multiplication would make us exactly backwards from what Matlab and
+ Julia users expect.
* APL apparently used ``+.×``, which by combining a multicharacter
token, confusing attributeaccesslike . syntax, and a unicode
@@ 863,7 +858,7 @@
``[*]`` or ``*`` (but when used in context, the use of vertical
grouping symbols tends to recreate the nested parentheses visual
clutter that was noted as one of the major downsides of the function
 syntax we're trying to get away from); ``^*`` and ``^^``.
+ syntax we're trying to get away from); ``^*``.
So, it doesn't matter much, but ``@`` seems as good or better than any
of the alternatives:
@@ 889,6 +884,81 @@
* Whatever, we have to pick something.
+Precedence and associativity
+
+
+There was a long discussion [#associativitydiscussions]_ about
+whether ``@`` should be right or leftassociative (or even something
+more exotic [#groupassociativity]_). Almost all Python operators are
+leftassociative, so following this convention would be the simplest
+approach, but there were two arguments that suggested matrix
+multiplication might be worth making rightassociative as a special
+case:
+
+First, matrix multiplication has a tight conceptual association with
+function application/composition, so many mathematically sophisticated
+users have an intuition that an expression like :math:`R S x` proceeds
+from righttoleft, with first :math:`S` transforming the vector
+:math:`x`, and then :math:`R` transforming the result. This isn't
+universally agreed (and not all numbercrunchers are steeped in the
+puremath conceptual framework that motivates this intuition
+[#oilindustryversusrightassociativity]_), but at the least this
+intuition is more common than for other operations like :math:`2 \cdot
+3 \cdot 4` which everyone reads as going from lefttoright.
+
+Second, if expressions like ``Mat @ Mat @ vec`` appear often in code,
+then programs will run faster (and efficiencyminded programmers will
+be able to use fewer parentheses) if this is evaluated as ``Mat @ (Mat
+@ vec)`` then if it is evaluated like ``(Mat @ Mat) @ vec``.
+
+However, weighing against these arguments are the following:
+
+Regarding the efficiency argument, empirically, we were unable to find
+any evidence that ``Mat @ Mat @ vec`` type expressions actually
+dominate in reallife code. Parsing a number of large projects that
+use numpy, we found that when forced by numpy's current funcall syntax
+to choose an order of operations for nested calls to ``dot``, people
+actually use leftassociative nesting slightly *more* often than
+rightassociative nesting [#numpyassociativitycounts]_. And anyway,
+writing parentheses isn't so bad  if an efficiencyminded programmer
+is going to take the trouble to think through the best way to evaluate
+some expression, they probably *should* write down the parentheses
+regardless of whether they're needed, just to make it obvious to the
+next reader that they order of operations matter.
+
+In addition, it turns out that other languages, including those with
+much more of a focus on linear algebra, overwhelmingly make their
+matmul operators leftassociative. Specifically, the ``@`` equivalent
+is leftassociative in R, Matlab, Julia, IDL, and Gauss. The only
+exceptions we found are Mathematica, in which ``a @ b @ c`` would be
+parsed nonassociatively as ``dot(a, b, c)``, and APL, in which all
+operators are rightassociative. There do not seem to exist any
+languages that make ``@`` rightassociative and ``*``
+leftassociative. And these decisions don't seem to be controversial
+ I've never seen anyone complaining about this particular aspect of
+any of these other languages, and the leftassociativity of ``*``
+doesn't seem to bother users of the existing Python libraries that use
+``*`` for matrix multiplication. So, at the least we can conclude from
+this that making ``@`` leftassociative will certainly not cause any
+disasters. Making ``@`` rightassociative, OTOH, would be exploring
+new and uncertain ground.
+
+And another advantage of leftassociativity is that it is much easier
+to learn and remember that ``@`` acts like ``*``, than it is to
+remember first that ``@`` is unlike other Python operators by being
+rightassociative, and then on top of this, also have to remember
+whether it is more tightly or more loosely binding than
+``*``. (Rightassociativity forces us to choose a precedence, and
+intuitions were about equally split on which precedence made more
+sense. So this suggests that no matter which choice we made, noone
+would be able to guess or remember it.)
+
+On net, therefore, the general consensus of the numerical community is
+that while matrix multiplication is something of a special case, it's
+not special enough to break the rules, and ``@`` should parse like
+``*`` does.
+
+
(Non)Definitions for builtin types

@@ 930,32 +1000,7 @@
decided that the utility of this was sufficiently unclear that it
would be better to leave it out for now, and only revisit the issue if
 once we have more experience with ``@``  it turns out that ``@@``
is truly missed. [#atatdiscussion]


Unresolved issues


Associativity of ``@``
''''''''''''''''''''''

It's been suggested that ``@`` should be rightassociative, on the
grounds that for expressions like ``Mat @ Mat @ vec``, the two
different evaluation orders produce the same result, but the
rightassociative order ``Mat @ (Mat @ vec)`` will be faster and use
less memory than the leftassociative order ``(Mat @ Mat) @ vec``.
(Matrixvector multiplication is much cheaper than matrixmatrix
multiplication). It would be a shame if users found themselves
required to use an overabundance of parentheses to achieve acceptable
speed/memory usage in common situations, but, it's not currently clear
whether such cases actually are common enough to override Python's
general rule of leftassociativity, or even whether they're more
common than the symmetric cases where leftassociativity would be
faster (though this does seem intuitively plausible). The only way to
answer this is probably to do an audit of some realworld uses and
check how often the associativity matters in practice; if this PEP is
accepted in principle, then we should probably do this check before
finalizing it.
+is truly missed. [#atatdiscussion]_
Rejected alternatives to adding a new operator
@@ 1130,8 +1175,9 @@
* numpydiscussion thread on whether to keep ``@@``:
http://mail.scipy.org/pipermail/numpydiscussion/2014March/069448.html
* numpydiscussion thread on precedence/associativity of ``@``:
 http://mail.scipy.org/pipermail/numpydiscussion/2014March/069444.html
+* numpydiscussion threads on precedence/associativity of ``@``:
+ * http://mail.scipy.org/pipermail/numpydiscussion/2014March/069444.html
+ * http://mail.scipy.org/pipermail/numpydiscussion/2014March/069605.html
References
@@ 1207,10 +1253,10 @@
Matrix multiply counts were estimated by counting how often certain
tokens which are used as matrix multiply function names occurred in
 each package. In principle this could create false positives, but
 as far as I know the counts are exact; it's unlikely that anyone is
 using ``dot`` as a variable name when it's also the name of one of
 the most widelyused numpy functions.
+ each package. This creates a small number of false positives for
+ scikitlearn, because we also count instances of the wrappers
+ around ``dot`` that this package uses, and so there are a few dozen
+ tokens which actually occur in ``import`` or ``def`` statements.
All counts were made using the latest development version of each
project as of 21 Feb 2014.
@@ 1312,6 +1358,21 @@
elementwise multiplication, and ``%`` for matrix multiplication:
https://mail.python.org/pipermail/matrixsig/1995August/000002.html
+.. [#atatdiscussion] http://mail.scipy.org/pipermail/numpydiscussion/2014March/069502.html
+
+.. [#associativitydiscussions]
+ http://mail.scipy.org/pipermail/numpydiscussion/2014March/069444.html
+ http://mail.scipy.org/pipermail/numpydiscussion/2014March/069605.html
+
+.. [#oilindustryversusrightassociativity]
+ http://mail.scipy.org/pipermail/numpydiscussion/2014March/069610.html
+
+.. [#numpyassociativitycounts]
+ http://mail.scipy.org/pipermail/numpydiscussion/2014March/069578.html
+
+.. [#groupassociativity]
+ http://mail.scipy.org/pipermail/numpydiscussion/2014March/069530.html
+
Copyright
=========

Repository URL: http://hg.python.org/peps
More information about the Pythoncheckins
mailing list