
On Thu, Jan 13, 2011 at 9:21 AM, Luc Goossens <luc.goossens@cern.ch> wrote:
Sorry maybe this was not clear from my mail but I am not so much interested in possible work-arounds but in why this asymmetry exists in the first place. I mean is there a reason as to why it is the way it is, or is it just that nobody ever asked for anything else.
There are two kinds of asymmetry here. One is semantic and one is syntactic. 1. Semantically, function calls are fundamentally asymmetric in Python. A call takes as its input a tuple of arguments and a dictionary of keyword arguments, but its output is either a single return value or a single raised exception. 2. Syntactically, the syntax for composing a value (tuple expressions, list/set/dict displays, constructor calls) differs from the syntax for decomposing a value into its parts (unpacking assignment). The ML family of programming languages eliminate both asymmetries about as completely as I can imagine. ML functions take one argument and return one value; either can be a tuple. The same pattern-matching syntax is used to cope with parameters and return values. To a very great degree the syntax for composing a tuple, record, or list is the same as the syntax for decomposing it. So what you're asking is at least demonstrably possible, at least for other languages. So why does Python have these asymmetries? 1. The semantic asymmetry (functions taking multiple parameters but returning a single value) is a subtle thing. Even in Scheme, where conceptual purity and treating continuations as procedures are core design principles of the entire language, this asymmetry is baked into (lambda) and the behavior of function calls. And even in ML there is *some* asymmetry; a function can die with an error rather than return anything. (You can "error out" but not "error in".) In Python's design, I imagine Guido found this particular asymmetry made the language fit the brain better. It's more like C. The greater symmetry in languages like ML may have felt like be too much--and one more unfamiliar thing for new users to trip over. In any case it would be impractical to change this in Python. It's baked into the language, the implementation, and the C API. 2. The syntactic asymmetry is made up of lots of little asymmetries, and I think it's enlightening to take a few of them case by case. (a) You can write [a, b] = [1, 2] but not {a, b} = {1, 2} or {"A": a, "B": b} = {"A": 1, "B": 2} Sets have no deterministic order, so the second possibility is misleading. The third is not implemented, I imagine, purely for usability reasons: it would do more harm than good. (b) You can write x = complex(1, 2) but not complex(a, b) = x In ML-like languages, you can identify constructors at compile time, so it's clear on the left-hand side of something like this what variables are being defined. In Python it's not so obvious what this is supposed to do. (c) Unlike ML, you can write (a, b) = [1, 2] or generally a, b = any_iterable It is useful for unpacking to depend on the iterable protocol rather than the exact type of the right-hand side. This is a nicety that ML-like languages don't bother with, afaik. (d) You can write def f(x, y, a) : ... f(**dct) but not (x, y, a) = **dct and conversely you can write lst[1] = x but not def f(lst[1]): ... f(x) In both cases, I find the accepted syntax sane, and the symmetric-but-currently-not-accepted syntax baffling. Note that in the case of lst[1] = x, we are mutating an existing object, something ML does not bother to make easy. All four of these cases seem to boil down to what's useful vs. what's confusing. You could go on for some time in that vein. Hope this helps. -j