Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Verbose and flexible args and kwargs syntax

200 views
Skip to first unread message

Eelco Hoogendoorn

unread,
Dec 11, 2011, 5:49:23 AM12/11/11
to pytho...@python.org
Throwing an idea for a PEP out there:

It strikes me that the def func(*args, **kwargs) syntax is rather
unpytonic. It certainly did not have that 'for line in file' pythonic
obviousness for me as a beginner. Plus, asterikses are impossible to
google for, so finding out what exactly they do more or less forces you
to write a forum post about it.

A more readable form occurred to me, which also happens to be more
flexible, and which I think is fully compatible with the syntax of the
language:

def func(parg, list(args), dict(kwargs))

Perhaps this is considered abuse of notation; dict(kwargs) already has a
meaning rather different from the one we try to give it here; but then
again the context (being inside a function definition) is unique and
easily recognizable.

An added advantage would be the possibility of using subclasses of dict
and list as well; imagine how much more beautiful a lot of code would be
if one could say

def func(attrdict(kwargs))

Problems im still wrestling with: the same syntax could not be used when
calling a function; that lack of symmetry would make things more
confusing, not less.

Thoughts?

Chris Angelico

unread,
Dec 11, 2011, 6:15:41 AM12/11/11
to pytho...@python.org
On Sun, Dec 11, 2011 at 9:49 PM, Eelco Hoogendoorn
<hoogendo...@gmail.com> wrote:
> Problems im still wrestling with: the same syntax could not be used when
> calling a function; that lack of symmetry would make things more confusing,
> not less.

That symmetry is a large factor, IMHO. I can write a wrapper function like this:

def fixedargs(x,y,z):
return wrappedfunc(x,y,z)

Or like this:

def anyargs(*args,**kwargs):
return wrappedfunc(*args,**kwargs)

Either way, it's a perfect parallel, and that's very useful for noticing errors.

With a keyworded syntax, that's going to be a lot harder.

Another issue: You suggest being able to use "attrdict" or some other
dict subclass. This means that, rather than being a language
construct, this will involve a name lookup. And what happens if you
have a class that subclasses both list and dict? Will it get the
positional args, the keyword args, or both?

Laudable notion, but I'm not sure that it'll actually work in practice.

ChrisA

Duncan Booth

unread,
Dec 11, 2011, 7:39:44 AM12/11/11
to
Chris Angelico <ros...@gmail.com> wrote:
> Either way, it's a perfect parallel, and that's very useful for
> noticing errors.
>
> With a keyworded syntax, that's going to be a lot harder.

If it used keywords then you could keep symmetry quite easily:

def anyargs(arglist args, argdict kwargs):
return wrappedfunc(arglist args, argdict kwargs)

and you would have the advantage of two new keywords that people could
actually search on.

>
> Another issue: You suggest being able to use "attrdict" or some other
> dict subclass. This means that, rather than being a language
> construct, this will involve a name lookup. And what happens if you
> have a class that subclasses both list and dict? Will it get the
> positional args, the keyword args, or both?

Irrelevant, you can't subclass both list and dict:

>>> class C(list, dict): pass

Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
class C(list, dict): pass
TypeError: multiple bases have instance lay-out conflict



--
Duncan Booth http://kupuguy.blogspot.com

Chris Angelico

unread,
Dec 11, 2011, 8:09:56 AM12/11/11
to pytho...@python.org
On Sun, Dec 11, 2011 at 11:39 PM, Duncan Booth
<duncan...@invalid.invalid> wrote:
> Chris Angelico <ros...@gmail.com> wrote:
> If it used keywords then you could keep symmetry quite easily:
>
>    def anyargs(arglist args, argdict kwargs):
>        return wrappedfunc(arglist args, argdict kwargs)
>
> and you would have the advantage of two new keywords that people could
> actually search on.

Yes, that's just a strict keywordification of the * and ** symbols.
The same argument could be made for eliminating the standard algebraic
+ operator and replacing it with a keyword "__add__". I don't think
that's worthwhile.

The OP suggested using 'dict' and 'list' themselves as the keywords,
thus allowing the use of subclasses. This would make it unsearchable,
or else rather verbose:

def anyargs(pack list args, pack dict kwargs):
return wrappedfunc(pack list args, pack dict kwargs)

With this syntax, what happens if you muck up list/dict? Or
alternatively, the briefer syntax:

def anyargs(pack list args, pack dict kwargs):
return wrappedfunc(pack args, pack kwargs)

which breaks the symmetry, and doesn't say which one you're doing -
for instance, if you only use one out of list and dict args, it would
make sense to have a variable "options" or "args" that gets unpacked
to the function's arguments - and there's no way to see whether it's
going to be keyword or positional.

The verbose syntax has a lot going for it, but it's rather verbose.
Why say "pack list args" when you can just say "*args"? Compare
initializer syntax between Python and PHP:

foo = ["asdf", "qwer", {1:2, 3:4}]

$foo = array("asdf", "qwer", array(1=>2, 3=>4))

Is it more Pythonic to use explicitly-named types, or to have simple
notation that's clear and easy to read? Or is this a matter for
personal preference?

>> Another issue: You suggest being able to use "attrdict" or some other
>> dict subclass. This means that, rather than being a language
>> construct, this will involve a name lookup. And what happens if you
>> have a class that subclasses both list and dict? Will it get the
>> positional args, the keyword args, or both?
>
> Irrelevant, you can't subclass both list and dict:
> TypeError: multiple bases have instance lay-out conflict

Ah. Curious. I've not done much with multiple inheritance in Python
(come to think of it, I don't recall when I last used MI in _any_
language). In any case, there's still the potential unclarity as to
_which_ of dict and list is the one that's been inherited, which
requires a run-time lookup to solve.

ChrisA

Christian Heimes

unread,
Dec 11, 2011, 8:14:13 AM12/11/11
to pytho...@python.org
Am 11.12.2011 13:39, schrieb Duncan Booth:
>> Another issue: You suggest being able to use "attrdict" or some other
>> dict subclass. This means that, rather than being a language
>> construct, this will involve a name lookup. And what happens if you
>> have a class that subclasses both list and dict? Will it get the
>> positional args, the keyword args, or both?
>
> Irrelevant, you can't subclass both list and dict:

But you can fake it with __instancecheck__ and __subclasscheck__ methods.

Christian

Eelco Hoogendoorn

unread,
Dec 11, 2011, 9:44:37 AM12/11/11
to pytho...@python.org
> Yes, that's just a strict keywordification of the * and ** symbols.
> The same argument could be made for eliminating the standard algebraic
> + operator and replacing it with a keyword "__add__". I don't think
> that's worthwhile.


Well, its not quite the same in the sense that algebraic operators are
essentially part of 'natural language', or at least extremely widely
adopted. They have earned their own special symbols. Argument
packing/unpacking is a very specific thing; a small corner of a
particular programming language.

However, as seen in the light of python 3 head-tail syntax, perhaps the
above is not quite true, and one could argue that packing/unpacking of
collections is indeed a quite general concept, deserving of its own
symbols. Breaking uniformity with that use case would also be a bad
thing; ideally, a verbose alternative to all occurances of collection
packing/unpacking would be available.

That said, a more verbose and flexible syntax would be desirable there
too; as of now, the tail is always a list. I havnt read the discussions
leading up to those design decisions, but that seems like a compromise
to me; something like head,tuple(tail) = someiterable would be
preferrable there too, id say

Eelco Hoogendoorn

unread,
Dec 11, 2011, 9:58:18 AM12/11/11
to pytho...@python.org
As for syntax; what about coopting/integrating with the function
annotation syntax?

so:

def func(args: list, kwargs: attrdict)
and correspondingly in the function call?

a, b:tuple = someiterable?

I guess a rule that interprets every function argument annotated as a
subclass of list or dict as a special case would severely restrict its
intended use though...

Steven D'Aprano

unread,
Dec 11, 2011, 5:19:42 PM12/11/11
to
On Sun, 11 Dec 2011 11:49:23 +0100, Eelco Hoogendoorn wrote:

> Throwing an idea for a PEP out there:
>
> It strikes me that the def func(*args, **kwargs) syntax is rather
> unpytonic. It certainly did not have that 'for line in file' pythonic
> obviousness for me as a beginner. Plus, asterikses are impossible to
> google for, so finding out what exactly they do more or less forces you
> to write a forum post about it.

No more so than any other form of punctuation. Plus and minus + - may be
so common that just about everyone knows it, but how about | == @ % and
even . (dot)? None of these things will be obvious to newbies who have
never programmed before. Oh well.

Some things you just have to learn.

As for impossibility of googling for asterisks, it might help if you
spell it correctly <wink>

http://duckduckgo.com/?q=python+asterisk

Fourth hit says:

*vargs puts all left-over non-keyword arguments into a
tuple called vargs. **kargs puts all left-over keyword
arguments into a dictionary called kargs.

Even better, this works amazingly well:

http://duckduckgo.com/?q=python+*

Or you can Read The Fine Manual, which has a section for special symbols:

http://docs.python.org/genindex-Symbols.html

which is moderately easy to discover from the main page: Main page =>
Documentation => Browse Current Documentation => General Index =>
Symbols. Not ideal, but not too bad.

> A more readable form occurred to me, which also happens to be more
> flexible, and which I think is fully compatible with the syntax of the
> language:
>
> def func(parg, list(args), dict(kwargs))
>
> Perhaps this is considered abuse of notation; dict(kwargs) already has a
> meaning rather different from the one we try to give it here; but then
> again the context (being inside a function definition) is unique and
> easily recognizable.

I consider that excessively verbose as well as misleading.

It is excessively verbose for the same reason as:

let y equal x plus 1

would be considered excessively verbose. I'm very fond of some languages
with verbose syntax, like Hypertalk and OpenXION where you might say "add
1 to x", but I don't want Python to become them.

It's a judgement call as to where a language divides "cryptic punctuation
line noise" and "useful short operators", and in my opinion * and ** tuple
and dict unpacking fall strongly on the "useful short operators" side.
Your opinion may differ, but luckily for me, the BDFL agrees with me :)

It is also misleading because args are not collected into a list, but
into a tuple. Worse, it suggests that one should be able to generalise to
something like this:

def func(parg, str(args), int(kwargs), my_func(more_args)):

which is incoherent.

And lastly, this would require either promoting list and dict to proper
keywords, which will not happen, or worse, making them semi-keywords
(treated as keywords in some contexts, but not in others), which also
will not happen.

> Problems im still wrestling with: the same syntax could not be used when
> calling a function; that lack of symmetry would make things more
> confusing, not less.

Precisely.


> Thoughts?

People learn to spell strings "like this" instead of str(like this), and
to use # for comments instead of REMARK or REM. It's not that much of a
burden on them to learn to use * and **


--
Steven

Terry Reedy

unread,
Dec 11, 2011, 6:26:39 PM12/11/11
to pytho...@python.org
On 12/11/2011 5:49 AM, Eelco Hoogendoorn wrote:

> Plus, asterikses are impossible to google for, so finding out what exactly
> they do more or less forces you to write a forum post about it.

There are other means of finding information than Google. Really.
1. A couple of years ago, I wrote a very complete list of symbol syntax
uses in Python 3. You can get is at
https://xploro.googlecode.com/files/PySymbols.html
Unfortunately, searching for 'python syntax symbols' does not show this
on the first few pages.
2. The python docs have an index. While the Symbols page does not have
most of the entries in the above doc, it does have '* in function calls'
and '** in function calls'.
3. The meaning of * and ** in function calls is explained, surprise, in
the reference manual section (5.3.4 for Py3) on function calls, which is
in the chapter on expressions.

--
Terry Jan Reedy

Eelco Hoogendoorn

unread,
Dec 11, 2011, 6:44:38 PM12/11/11
to pytho...@python.org
> No more so than any other form of punctuation. Plus and minus + - may be
> so common that just about everyone knows it, but how about | == @ % and
> even . (dot)? None of these things will be obvious to newbies who have
> never programmed before. Oh well.

> Some things you just have to learn.


Yes, some things you just have to learn. Nonetheless, I strongly prefer
explicit logical operators over |, would much rather have 'equals'
instead of ==, which is stylistic in line with 'is' and explicitly
distinguishes between equality and identity comparisons. As for %; it is
entirely unclear to me why that obscure operation ever got its own
one-character symbol. Ill take 'mod', or even better, 'modulus' any day
of the week.

The dot is clearly quantitatively in another realm here. 90% of typical
python code is attribute accesses. The dot is entirely unambigious and
cannot be mistaken for anything else. It reads like a book.

> It's a judgement call as to where a language divides "cryptic punctuation
> line noise" and "useful short operators", and in my opinion * and ** tuple
> and dict unpacking fall strongly on the "useful short operators" side.
> Your opinion may differ, but luckily for me, the BDFL agrees with me :)

I also agree that it is a value judgement as to which constructs get
their own cryptic symbols and which do not, but the are some reasonable
guidelines we should be able to agree upon. Obscure operations should
not reserve any of the few available characters. Furthermore, the
language should not just be formally consistent, but also easy to grasp
at a glance, without deciphering subtle semantics of a blurb of weird
characters. (some programming languages obviously disagree, but python,
as far as I am allowed to speak for it, does not). And most importantly,
if you cant come up with a terse syntax that does everything you want to
do, the terse syntax should at best be an alternative to the verbose one.


> It is also misleading because args are not collected into a list, but
> into a tuple.

In case you wanted a tuple youd write tuple(args), obviously. Exactly that added flexibility is half of my case in favor. Why shouldnt it be a list when I want it to?



> Worse, it suggests that one should be able to generalise to
> something like this:

> def func(parg, str(args), int(kwargs), my_func(more_args)):

> which is incoherent.

Sorry, but I dont get this point at all. Does ** suggests one should be
able to generalize to ***? The rules are the rules.



The real questions, in my mind, are:

1) How useful is this added flexibility? Not insanely, but I can see it
making a lot of code significantly more clean.

And:

2) How fundamental is collection packing/unpacking? One can easily argue
that it is indeed quite fundamental and therefore deserves its own terse
symbols, I feel. However, if more flexibility is indeed deemed
desirable, such terse syntax quickly gives way to a more verbose one.
Can you come up with some terse symbols that will be able to express all
of the below and dont make you wish you hadnt rather typed out the names?

head, tuple(tail) = iterable
head, list(tail) = iterable
head, str(tail) = somestring
head, generator(tail) = mygenerator

And so on.

If not, one has to admit that functionality is being sacrificed on the
alter of terseness, which seems like a raw deal to me.

Eelco Hoogendoorn

unread,
Dec 11, 2011, 6:53:22 PM12/11/11
to pytho...@python.org
> There are other means of finding information than Google. Really.

This is really only a very minor point in my argument, so I dont want to
put the focus on this.

But really, no.

Googling 'myprogramminglanguage conceptimtryingtofigureout' is my first,
second and third line of defence. Yes, I could read the reference manual
from top to bottom, and if I already knew about the existence of your
article then im sure that would be a great help too. But the situation
one finds oneself in is seeing two asterikses and not even being aware
they are particular to function definitions/invocations. Im fluent in
many different languages and well versed in CS concepts and jargon, but
I had no idea what to search for when first trying to figure out the
meaning of these symbols, and that does not happen often to me.

Chris Angelico

unread,
Dec 11, 2011, 7:37:26 PM12/11/11
to pytho...@python.org
On Mon, Dec 12, 2011 at 10:53 AM, Eelco Hoogendoorn
<hoogendo...@gmail.com> wrote:
> Googling 'myprogramminglanguage conceptimtryingtofigureout' is my first,
> second and third line of defence. Yes, I could read the reference manual
> from top to bottom, and if I already knew about the existence of your
> article then im sure that would be a great help too.

Python in particular has very poor internal search. I don't recommend
searching the Python docs for anything that you can search for
externally using Google, duckduckgo, etc, etc.

ChrisA

Steven D'Aprano

unread,
Dec 11, 2011, 8:11:00 PM12/11/11
to
On Mon, 12 Dec 2011 00:44:38 +0100, Eelco Hoogendoorn wrote:

>> No more so than any other form of punctuation. Plus and minus + - may
>> be so common that just about everyone knows it, but how about | == @ %
>> and even . (dot)? None of these things will be obvious to newbies who
>> have never programmed before. Oh well.
>
>> Some things you just have to learn.
>
>
> Yes, some things you just have to learn. Nonetheless, I strongly prefer
> explicit logical operators over |, would much rather have 'equals'
> instead of ==, which is stylistic in line with 'is' and explicitly
> distinguishes between equality and identity comparisons.

No more, or less, explicit than the difference between "==" and "is".


> As for %; it is
> entirely unclear to me why that obscure operation ever got its own
> one-character symbol. Ill take 'mod', or even better, 'modulus' any day
> of the week.

Modulo is hardly an obscure operation. "What's the remainder...?" is a
simple question that people learn about in primary school.

And you can blame C for the use of % instead of mod or modulo.


> The dot is clearly quantitatively in another realm here. 90% of typical
> python code is attribute accesses.

I can't imagine what sort of Python code you have seen that you consider
90% attribute access "typical". I've just run the Python tokenizer over
my startup.py file, and I get these results:

{'COMMENT': 24, 'DEDENT': 29, 'NL': 46, 'NAME': 256, "':'": 30,
'NEWLINE': 83, "'-'": 1, 'NUMBER': 1, "'['": 1, "','": 17, "')'": 37,
"'('": 37, "'%'": 2, "'.'": 48, "'=='": 1, "'*'": 1, 'INDENT': 29, "']'":
1, "'='": 28, 'ENDMARKER': 1, 'STRING': 19}

That gives attribute access being a little less than 7% of the source
code. For the decimal module, the figure is a little less than 5%.


> The dot is entirely unambigious and
> cannot be mistaken for anything else. It reads like a book.

The dot can be easily mistaken for a comma, or for a bit of grit on the
monitor, especially at smaller type sizes, or for those with poor
eyesight.


[...]
>> It is also misleading because args are not collected into a list, but
>> into a tuple.
>
> In case you wanted a tuple youd write tuple(args), obviously. Exactly
> that added flexibility is half of my case in favor. Why shouldnt it be a
> list when I want it to?

What sort of list? A built-in list, or whatever sort of object you get
when you call the thing currently bound to the name "list"?

If you can supply any function at all, what happens if I write this:

def func(parg, dict(foo), list(bar)): ...

How about this?

def func(parg, myfunc(x)): ...

What is x now? Should Python try to accumulate arguments by position, or
by keyword, or try both and hope one will succeed? Which order should it
try first?

I believe that your proposal leads to an over-generalisation "call
arbitrary functions when handling parameter lists". I don't believe you
need this added complication. If you want to your var args as a list,
call list(args) inside your function.


>> Worse, it suggests that one should be able to generalise to something
>> like this:
>
>> def func(parg, str(args), int(kwargs), my_func(more_args)):
>
>> which is incoherent.
>
> Sorry, but I dont get this point at all. Does ** suggests one should be
> able to generalize to ***? The rules are the rules.

You have missed that the generalization is not just to multiple "chunks"
of arguments, but also to arbitrary functions. I thought that both ideas
were equally incoherent, but ironically you suggest about that you should
be able to call arbitrary functions: tuple, list, attrdict. What else?
str? int?


> The real questions, in my mind, are:
>
> 1) How useful is this added flexibility? Not insanely, but I can see it
> making a lot of code significantly more clean.

I don't. I see it making a small amount of code more verbose and less
clean.


> And:
>
> 2) How fundamental is collection packing/unpacking? One can easily argue
> that it is indeed quite fundamental and therefore deserves its own terse
> symbols, I feel.

In Python, tuple unpacking and packing (actually applies to any
collection, not just tuples) is *very* fundamental. That's why we can do
things like this:

a, b, c = my_list
x, y = y, x

> However, if more flexibility is indeed deemed
> desirable, such terse syntax quickly gives way to a more verbose one.
> Can you come up with some terse symbols that will be able to express all
> of the below and dont make you wish you hadnt rather typed out the
> names?
>
> head, tuple(tail) = iterable

In Python 3, that is spelled:

head, *tail = iterable
tail = tuple(tail)


> head, list(tail) = iterable

head, *tail = iterable


> head, str(tail) = somestring

This is ambiguous, I'm not sure what exactly you expect to get as the
string. It could arguable be any of:

tail = ''.join(map(repr, tail))
tail = ''.join(map(str, tail))
tail = str(tail)

or even

head, tail = somestring[0], somestring[1:]


> head, generator(tail) = mygenerator

And this is most easily spelled:

head, tail = next(mygenerator), mygenerator



--
Steven

Gregory Ewing

unread,
Dec 12, 2011, 3:09:43 AM12/12/11
to
Steven D'Aprano wrote:

> Modulo is hardly an obscure operation. "What's the remainder...?" is a
> simple question that people learn about in primary school.

Well, sort of. The way I remember it, the remainder was just
something that fell out as a side effect of division -- the
annoying bit left over that you didn't know what to do with.
We weren't taught to think of "finding the remainder" as
a distinct operation that's useful in its own right. Once
we were taught to do proper division with decimal points
and everything, the concept of a remainder seemed to get
discarded and was never mentioned again.

A couple of years later we were briefly introduced to the
concept of modulo arithmetic, but as far as I remember, the
connection with division and remainders wasn't pointed out.
Also, it was presented in a very abstract way, and I couldn't
see any practical application for it at the time. (At that
age, it hadn't occurred to me that some of the stuff we
were being shown might be just pure mathematics for its own
sake, and I was often thinking to myself, "Why am I being
taught this?")

It wasn't until much later when I got into programming that
I began to see all the connections and applications. For
people who don't become programmers, I suspect they never
have much use for remainders in everyday life.

Now, in a desperate attempt to stop rambling and get back
on topic...

> Eelco Hoogendoorn wrote:
>>The dot is clearly quantitatively in another realm here.

Also it has almost unchallenged supremacy as the attribute
access operator in just about every language having some
form of struct concept, going back to around Algol 68.
Spelling of the mod operator is much more variable.

> {'COMMENT': 24, 'DEDENT': 29, 'NL': 46, 'NAME': 256, "':'": 30,
> 'NEWLINE': 83, "'-'": 1, 'NUMBER': 1, "'['": 1, "','": 17, "')'": 37,
> "'('": 37, "'%'": 2, "'.'": 48, "'=='": 1, "'*'": 1, 'INDENT': 29, "']'":
> 1, "'='": 28, 'ENDMARKER': 1, 'STRING': 19}
>
> That gives attribute access being a little less than 7% of the source
> code.

However, it's clearly the most commonly used *operator* by
a large margin.

> The dot can be easily mistaken for a comma,

Not in my code, because I always put a space after a comma,
and never after an attribute-access dot. (And if you can't
tell whether there's a space there or not, you need a
bigger font or better glasses. :-)

Also, dots sit nicely under my little finger where they're
easy to type. I like dots. Dots are a great goodness.

--
Greg

Gregory Ewing

unread,
Dec 12, 2011, 3:25:40 AM12/12/11
to
For what it's worth, googling for "python asterisk"
gives this as the very first result:

http://www.technovelty.org/code/python/asterisk.html

which tells you exactly what you're probably wanting
to know if you ask that.

To check that this phenomemon isn't restricted to
asterisks in particular, I also tried "python plus
equals" and got

http://stackoverflow.com/questions/2347265/what-does-plus-equals-do-in-python

which is also a pretty good result.

So the rule of thumb seems to be: if you're trying to
google for punctuation, try spelling it out.

--
Greg

Terry Reedy

unread,
Dec 12, 2011, 3:29:53 AM12/12/11
to pytho...@python.org
On 12/11/2011 6:53 PM, Eelco Hoogendoorn wrote:
>> There are other means of finding information than Google. Really.
>
> This is really only a very minor point in my argument, so I dont want to
> put the focus on this.

On the contrary, it is a major point. You want us to change the language
so you can program by Google. Sorry, aint't gonna happen.

> Googling 'myprogramminglanguage conceptimtryingtofigureout' is my first,
> second and third line of defence. Yes, I could read the reference manual
> from top to bottom, and if I already knew about the existence of your
> article then im sure that would be a great help too.

You left out skimming the table of contents and using the index. On the
Windows version of the docs, one can just type the entry wanted in the
entry box on the Index tab and the lookup is done for you. Two chars to
type for '**'.

> But the situation
> one finds oneself in is seeing two asterikses and not even being aware
> they are particular to function definitions/invocations.

If you find a symbol in a particular context, the entry for the context
seems a reasonable place to start.

--
Terry Jan Reedy

Terry Reedy

unread,
Dec 12, 2011, 3:44:51 AM12/12/11
to pytho...@python.org
On 12/11/2011 6:44 PM, Eelco Hoogendoorn wrote:

> Can you come up with some terse symbols that will be able to express all
> of the below and dont make you wish you hadnt rather typed out the names?
>
> head, tuple(tail) = iterable
> head, list(tail) = iterable
> head, str(tail) = somestring
> head, generator(tail) = mygenerator

The above examples are seldom needed in Python because we have one
general method to repeatedly split a sequence into head and tail.

it = iter(iterable) # 'it' now represents the sequenced iterable
head = next(it) # 'it' now represents the tail after removing the head

In other words, next(it) encompasses all of your examples and many more.
Because 'it' is mutated to represent the tail, it does not need to be
rebound and therefore is not.

Iterable unpacking with a *target for leftovers is an entirely different
use case.

--
Terry Jan Reedy

Eelco Hoogendoorn

unread,
Dec 12, 2011, 4:12:22 AM12/12/11
to pytho...@python.org
> The above examples are seldom needed in Python because we have one
> general method to repeatedly split a sequence into head and tail.

> it = iter(iterable) # 'it' now represents the sequenced iterable
> head = next(it) # 'it' now represents the tail after removing the head

> In other words, next(it) encompasses all of your examples and many more.
> Because 'it' is mutated to represent the tail, it does not need to be
> rebound and therefore is not.


The question in language design is never 'could we do these things
before'. The answer is obvious: yes our CPUs are turing complete; we can
do anything. The question is; how would we like to do them?

So do you think the new head/tail unpacking features in python 3 are
entirely uncalled for? I personally quite like them, but I would like
them to be more general.

Eelco Hoogendoorn

unread,
Dec 12, 2011, 4:37:40 AM12/12/11
to pytho...@python.org
> On the contrary, it is a major point. You want us to change the language
> so you can program by Google. Sorry, aint't gonna happen.

On the contrary; I believe I get to decide which points I consider
important. This one, I do not. Sorry for putting it in the first paragraph.

Eelco Hoogendoorn

unread,
Dec 12, 2011, 4:08:04 AM12/12/11
to pytho...@python.org
> On the contrary, it is a major point.

Sorry, but im affraid it is up to ME to decide which point I feel are
important. No, this is a minor point to me, and one that has been
admirably put to rest by pointing out that spelling out the name of the
symbol in google directly leads you to the information you are looking for.

Jussi Piitulainen

unread,
Dec 12, 2011, 5:59:05 AM12/12/11
to
Eelco Hoogendoorn writes:

> As for %; it is entirely unclear to me why that obscure operation
> ever got its own one-character symbol. Ill take 'mod', or even
> better, 'modulus' any day of the week.

The modulus is not the result but one of the arguments: when numbers x
and y are congruent modulo n (stated in terms of the modulo operation:
x mod n = y mod n), the modulus is n. A word for x mod n is remainder.

I agree about the obscurity of using the percent sign as the operator.

A quick google suggests that your use of 'modulus' is now popular
among programmers. Past experience in mathematics newsgroups tells me
that some mathematicians do not accept the existence of any remainder
operator at all. Honest. (I see them but I cannot understand them.)

Eelco

unread,
Dec 12, 2011, 6:22:25 AM12/12/11
to
> The modulus is not the result but one of the arguments: when numbers x
> and y are congruent modulo n (stated in terms of the modulo operation:
> x mod n = y mod n), the modulus is n. A word for x mod n is remainder.
>
> I agree about the obscurity of using the percent sign as the operator.
>
> A quick google suggests that your use of 'modulus' is now popular
> among programmers. Past experience in mathematics newsgroups tells me
> that some mathematicians do not accept the existence of any remainder
> operator at all. Honest. (I see them but I cannot understand them.)

You are correct; the thing it computes is the remainder, not the
modulus. Nonetheless, 'x modulus y' is how it is put in natural
language, but I suppose math.remainder would be my preferred place to
put this.

Eelco

unread,
Dec 12, 2011, 6:18:42 AM12/12/11
to

By the way...

Is there any particular reason why some of my replies do not show up
on groups.google, and some of them do not show up on mail.python.org?
Sorry to annoy people with reposting, but im going to be forced to do
some of that until this is cleared up....

Jussi Piitulainen

unread,
Dec 12, 2011, 6:46:56 AM12/12/11
to
I think it's 'x modulo y', which matches 'x and y are congruent modulo
z', but now I fear that programming people have been developing a
different habit.

Eelco

unread,
Dec 12, 2011, 7:21:15 AM12/12/11
to
> No more, or less, explicit than the difference between "==" and "is".

== may be taken to mean identity comparison; 'equals' can only mean
one
thing. Of course 'formally' these symbols are well defined, but so is
brainf*ck

> Modulo is hardly an obscure operation. "What's the remainder...?" is a
> simple question that people learn about in primary school.


So is 'how much wood would a woodchucker chuck if a woodchucker could
chuck wood?'. But how often does that concept turn up in your code?

> And you can blame C for the use of % instead of mod or modulo.

I didnt know one of Python's design goals was backwards compatibility
with C.

> I can't imagine what sort of Python code you have seen that you consider
> 90% attribute access "typical". I've just run the Python tokenizer over
> my startup.py file, and I get these results:

Yes, that was a hyperbole; but quite an often used construct, is it
not?

> If you can supply any function at all, what happens if I write this:


You cannot; only constructors modelling a sequence or a dict, and
only
in that order. Is that rule clear enough?

> I believe that your proposal leads to an over-generalisation "call
> arbitrary functions when handling parameter lists".

I hope the above clears that up. It is as much about calling
functions
as ** is about raising kwargs to the power of.

> I don't believe you
> need this added complication. If you want to your var args as a list,
> call list(args) inside your function.

We dont strictly 'need' any language construct. Real men use
assembler,
right?


> > head, tuple(tail) = iterable
> In Python 3, that is spelled:
> head, *tail = iterable
> tail = tuple(tail)

Yes, I know. How is that not a lot more verbose and worse than what I
have proposed in all possible ways?

> head, tail = somestring[0], somestring[1:]

Well yes, splendid; we can do that with lists too since the dawn of
Python. What you are saying here in effect is that you think the
head/tail syntax is superfluous; that youd rather see it eliminated
than
generalized.

> head, tail = next(mygenerator), mygenerator

Which again of course works, but is yet again of entirely different
form
than any of the above solutions, while conceptually doing the same
thing. Certainly, there is room for improved elegance here?

Terry Reedy

unread,
Dec 12, 2011, 10:36:36 AM12/12/11
to pytho...@python.org
On 12/12/2011 3:09 AM, Gregory Ewing wrote:

> people who don't become programmers, I suspect they never
> have much use for remainders in everyday life.

Huh? Funny you should say 'everyday'. It is now 10 o'clock. In 20 hours,
it will be (10+20) % 12 == 6 o'clock. It is now day 1 of the week. In 9
days it will be day (1+9) % 7 == 3 of the week. Mental calculations are
helped by the fact that (a+b) % c == a%c + b%c, so that would actually
be 1+2==3. Timekeeping is mostly remaindering, slightly obscured by
using 12 instead of 0.

--
Terry Jan Reedy

Terry Reedy

unread,
Dec 12, 2011, 10:39:48 AM12/12/11
to pytho...@python.org
On 12/12/2011 5:59 AM, Jussi Piitulainen wrote:


> Past experience in mathematics newsgroups tells me
> that some mathematicians do not accept the existence of any remainder
> operator at all.

Even though they carry hour/minute/second remindering devices on their
bodies and put year/month/day remaindering devices on their wall?
'Twould be strange indeed!

--
Terry Jan Reedy

Terry Reedy

unread,
Dec 12, 2011, 10:52:25 AM12/12/11
to pytho...@python.org
No, *target unpacking (singular) is quite useful in specialized cases.
But it is not specifically head/tail unpacking.

>>> a,*b,c = 1,2,3,4,5,6
>>> a,b,c
(1, [2, 3, 4, 5], 6)
>>> *a,b,c = 1,2,3,4,5
>>> a,b,c
([1, 2, 3], 4, 5)

> I personally quite like them, but I would like them to be more general.

It already is. The *target can be anywhere in the sequence.

--
Terry Jan Reedy

Nick Dokos

unread,
Dec 12, 2011, 10:55:44 AM12/12/11
to Terry Reedy, pytho...@python.org
Terry Reedy <tjr...@udel.edu> wrote:


> calculations are helped by the fact that (a+b) % c == a%c + b%c, so

As long as we understand that == here does not mean "equal", only
"congruent modulo c", e.g try a = 13, b = 12, c = 7.

Nick






Arnaud Delobelle

unread,
Dec 12, 2011, 11:10:00 AM12/12/11
to Terry Reedy, pytho...@python.org
On 12 December 2011 15:52, Terry Reedy <tjr...@udel.edu> wrote:

> No, *target unpacking (singular) is quite useful in specialized cases. But
> it is not specifically head/tail unpacking.
>
>>>> a,*b,c = 1,2,3,4,5,6
>>>> a,b,c
> (1, [2, 3, 4, 5], 6)
>>>> *a,b,c = 1,2,3,4,5
>>>> a,b,c
> ([1, 2, 3], 4, 5)
>
>> I personally quite like them, but I would like them to be more general.
>
>
> It already is. The *target can be anywhere in the sequence.
>
> --
> Terry Jan Reedy

You can even have nested sequences!

>>> a, (b, *c), *d = 1, "two", 3, 4

--
Arnaud

Arnaud Delobelle

unread,
Dec 12, 2011, 11:15:05 AM12/12/11
to Terry Reedy, pytho...@python.org
On 12 December 2011 15:36, Terry Reedy <tjr...@udel.edu> wrote:
> Huh? Funny you should say 'everyday'. It is now 10 o'clock. In 20 hours, it
> will be (10+20) % 12 == 6 o'clock. It is now day 1 of the week. In 9 days it
> will be day (1+9) % 7 == 3 of the week. Mental calculations are helped by
> the fact that (a+b) % c == a%c + b%c

You mean (a + b) % c == (a%c + b%c) % c

:)

--
Arnaud

Chris Angelico

unread,
Dec 12, 2011, 11:16:08 AM12/12/11
to pytho...@python.org
This is the basis of the grade-school "casting out nines" method of
checking arithmetic. Set c=9 and you can calculate N%c fairly readily
(digit sum - I'm assuming here that the arithmetic is being done in
decimal); the sum of the remainders should equal the remainder of the
sum, but there's the inherent assumption that if the remainders sum to
something greater than nine, you digit-sum it to get the true
remainder.

(Technically the sum of the digits of a base-10 number is not the same
as that number mod 9, but if you accept that 0 == 9, it works fine.)

ChrisA

Chris Angelico

unread,
Dec 12, 2011, 11:19:39 AM12/12/11
to pytho...@python.org
On Tue, Dec 13, 2011 at 3:15 AM, Arnaud Delobelle <arn...@gmail.com> wrote:
>
> You mean (a + b) % c == (a%c + b%c) % c
>
> :)

It's just integer wraparound. Modulo 9 is the same as "render this
number in base 9 and take the last digit" (and printing a number in
base 9 would normally be done with mod 9 division), and most people
can wrap their heads around the way an odometer wraps around.

ChrisA

Jussi Piitulainen

unread,
Dec 12, 2011, 11:52:49 AM12/12/11
to
They recognize modular arithmetic but for some reason insist that
there is no such _binary operation_. But as I said, I don't understand
their concern. (Except the related concern about some programming
languages, not Python, where the remainder does not behave well with
respect to division.)

Eelco

unread,
Dec 12, 2011, 12:12:10 PM12/12/11
to

> > I personally quite like them, but I would like them to be more general.
>
> It already is. The *target can be anywhere in the sequence.

Im not sure if this is a genuine understanding, or trollish
obtuseness.

Yes, the target can be anywhere in the sequence. And yes, the
resulting list can contain objects of any type, so its very flexible
in that regard too.

But to relate it to the topic of this thread: no, the syntax does not
allow one to select the type of the resulting sequence. It always
constructs a list.

Yes, we can cast the list to be whatever we want on the next line, but
the question is whether this language design can be improved upon. The
choice of a list feels arbitrary, adding another line to cast it to
something else would be even more verbose, and whats more, there would
be serious performance implications if one should seek to apply this
pattern to a deque/linkedlist; it would make taking off the head/tail
of the list from a constant to a linear operation.

That is:

>>> head, deque(tail) = somedeque

Is better in every way I can think of (readability, consistence,
performance) than:

>>> head, *tail = somedeque
>>> tail = deque(tail)

Eelco

unread,
Dec 12, 2011, 12:29:11 PM12/12/11
to
> They recognize modular arithmetic but for some reason insist that
> there is no such _binary operation_. But as I said, I don't understand
> their concern. (Except the related concern about some programming
> languages, not Python, where the remainder does not behave well with
> respect to division.)

They might not be willing to define it, but as soon as we programmers
do, well, we did.

Having studied the contemporary philosophy of mathematics, their
concern is probably that in their minds, mathematics is whatever some
dead guy said it was, and they dont know of any dead guy ever talking
about a modulus operation, so therefore it 'does not exist'.

Whatever you want to call the concept we are talking about, or whether
you care to talk about it at all, it is most certainly a binary
operation, since there are two arguments involved. There is no way
around that.

gene heskett

unread,
Dec 12, 2011, 12:46:07 PM12/12/11
to pytho...@python.org
On Monday, December 12, 2011 12:44:27 PM Chris Angelico did opine:
And that is precisely the reason I have failed to understand why the 1-10
decimal system seems to have hung on for several hundred years when it is
clearly broken.

Cheers, Gene
--
"There are four boxes to be used in defense of liberty:
soap, ballot, jury, and ammo. Please use in that order."
-Ed Howdershelt (Author)
My web page: <http://coyoteden.dyndns-free.com:85/gene>
Grub first, then ethics.
-- Bertolt Brecht

Nick Dokos

unread,
Dec 12, 2011, 12:58:09 PM12/12/11
to Jussi Piitulainen, pytho...@python.org
They are probably arguing that it's uniquely defined only on ZxN and
that there are different conventions to extend it to ZxZ (the programming
languages problem that you allude to above - although I don't know what you
mean by "does not behave well wrt division"). See

http://en.wikipedia.org/wiki/Remainder

If you choose one convention and stick to it, it becomes a well-defined
binary operation. C99 goes one way, python goes a different way (and
mathematics textbooks generally go a third way) and they are all happy,
as long as they don't try to talk to each other (e.g., porting C99
programs to python unthinkingly leads to trouble - duh). It was
implementation dependent in old C (whatever the hardware would give
you), which predictably - with 20-20 hindsight - turned out to be a Very
Bad Idea.

Nick

PS Z = integers, N = non-negative integers

Ian Kelly

unread,
Dec 12, 2011, 1:09:04 PM12/12/11
to Eelco, pytho...@python.org
On Mon, Dec 12, 2011 at 5:21 AM, Eelco <hoogendo...@gmail.com> wrote:
> You cannot; only constructors modelling a sequence or a dict, and
> only
> in that order. Is that rule clear enough?

The dict constructor can receive either a sequence or a mapping, so if
I write this:

def func(a, b, dict(c)):

what will I get? Probably I would want the equivalent of:

def func(a, b, **c):

but you seem to be saying that I would actually get the equivalent of this:

def func(a, b, *c):
c = dict(c)

Cheers,
Ian

Jussi Piitulainen

unread,
Dec 12, 2011, 1:09:48 PM12/12/11
to
Yes, I think you nailed it.

But I guess I'll still be confused the next time I meet one of them.
Happens to me. :)

Dave Angel

unread,
Dec 12, 2011, 1:04:34 PM12/12/11
to gene heskett, pytho...@python.org
On 12/12/2011 12:46 PM, gene heskett wrote:
> On Monday, December 12, 2011 12:44:27 PM Chris Angelico did opine:
> <snip>
>> This is the basis of the grade-school "casting out nines" method of
>> checking arithmetic. Set c=9 and you can calculate N%c fairly readily
>> (digit sum - I'm assuming here that the arithmetic is being done in
>> decimal); the sum of the remainders should equal the remainder of the
>> sum, but there's the inherent assumption that if the remainders sum to
>> something greater than nine, you digit-sum it to get the true
>> remainder.
>>
>> (Technically the sum of the digits of a base-10 number is not the same
>> as that number mod 9, but if you accept that 0 == 9, it works fine.)
>>
>> ChrisA
> And that is precisely the reason I have failed to understand why the 1-10
> decimal system seems to have hung on for several hundred years when it is
> clearly broken.
>
I assume this was facetious, but in case not, I'd point out that any
other number base will have similar modulo characteristics, except for
base 2, where all numbers are congruent modulo 1, so it doesn't do much
for checking values.

For example, if you were using a number system of base 8, you could do
"casting out sevens" by adding the digits together.

--

DaveA

Nick Dokos

unread,
Dec 12, 2011, 1:02:13 PM12/12/11
to gene heskett, pytho...@python.org
gene heskett <ghes...@wdtv.com> wrote:

> On Monday, December 12, 2011 12:44:27 PM Chris Angelico did opine:
>
> > On Tue, Dec 13, 2011 at 2:55 AM, Nick Dokos <nichola...@hp.com>
> wrote:
> > > Terry Reedy <tjr...@udel.edu> wrote:
> > >> calculations are helped by the fact that (a+b) % c == a%c + b%c, so
> > >
> > > As long as we understand that == here does not mean "equal", only
> > > "congruent modulo c", e.g try a = 13, b = 12, c = 7.
> >
> > This is the basis of the grade-school "casting out nines" method of
> > checking arithmetic. Set c=9 and you can calculate N%c fairly readily
> > (digit sum - I'm assuming here that the arithmetic is being done in
> > decimal); the sum of the remainders should equal the remainder of the
> > sum, but there's the inherent assumption that if the remainders sum to
> > something greater than nine, you digit-sum it to get the true
> > remainder.
> >
> > (Technically the sum of the digits of a base-10 number is not the same
> > as that number mod 9, but if you accept that 0 == 9, it works fine.)
> >
> > ChrisA
>
> And that is precisely the reason I have failed to understand why the 1-10

It's not clear from the above what you mean by "that is presicely the reason":
what is "that"?

> decimal system seems to have hung on for several hundred years when it is
> clearly broken.
>

"broken" how?

Thanks,
Nick



Eelco

unread,
Dec 12, 2011, 1:17:11 PM12/12/11
to
On Dec 12, 7:09 pm, Ian Kelly <ian.g.ke...@gmail.com> wrote:
Im not sure if I was clear on that, but I dont care what the
constructors accept; I meant to overload on the concept the underlying
type models. Dicts model a mapping, lists/tuples model a sequence. MI
deriving from both these models is illegal anyway, so one can
unambigiously overload on that trait.

The syntax only superficially resembles 'call the dict constructor
with the object passed into kwargs'. What its supposed to mean is
exactly the same as **kwargs, but with added flexibility.

Ian Kelly

unread,
Dec 12, 2011, 1:35:28 PM12/12/11
to Eelco, pytho...@python.org
On Mon, Dec 12, 2011 at 11:17 AM, Eelco <hoogendo...@gmail.com> wrote:
> Im not sure if I was clear on that, but I dont care what the
> constructors accept; I meant to overload on the concept the underlying
> type models. Dicts model a mapping, lists/tuples model a sequence. MI
> deriving from both these models is illegal anyway, so one can
> unambigiously overload on that trait.

False.

>>> from collections import *
>>> class Foo(Sequence, Mapping):
... def __init__(self, items):
... self._items = items
... def __getitem__(self, item):
... return self._items[item]
... def __len__(self):
... return len(self._items)
...
>>> foo1 = Foo(range(5, 10))
>>> foo2 = Foo({'one': 1, 'two': 2})
>>> foo1[3]
8
>>> foo2['one']
1

Or are you saying that only classes specifically derived from list,
tuple, or dict should be considered, and custom containers that are
not derived from any of those but implement the correct protocols
should be excluded? If so, that sounds less than ideal.

Cheers,
Ian

Eelco

unread,
Dec 12, 2011, 1:51:24 PM12/12/11
to
> False.

I stand corrected.

> Or are you saying that only classes specifically derived from list,
> tuple, or dict should be considered, and custom containers that are
> not derived from any of those but implement the correct protocols
> should be excluded?  If so, that sounds less than ideal.

That might be a desirable constraint from an implementational/
performance aspect anyway, but I agree, less than ideal.

Either way, its not hard to add some detail to the semantics to allow
all this. Even this function definition:

def func(Foo(args), Foo(kwargs))

...could even be defined unambigiously by overloading first on base
type, and if that does not uniquely determine the args and kwargs,
fall back on positionality, so that:

def func(Foo(args), dict(kwargs))
def func(list(args), Foo(kwargs))

would be uniquely defined as well.

Eelco

unread,
Dec 12, 2011, 2:05:26 PM12/12/11
to
To get back on topic a little bit, lets get back to the syntax of all
this: I think we all agree that recycling the function call syntax is
less than ideal, since while it works in special contexts like a
function signature, its symmetric counterpart inside a function call
already has the meaning of a function call.

In general, we face the problem of specifying metadata about a
variable, or a limited form of type constraint.

What we want is similar to function annotations in python 3; in line
with that, we could have more general variable annotations. With an
important conceptual distinction; function annotations are meaningless
to python, but the annotations I have in mind should modify semantics
directly. However, its still conceptually close enough that we might
want to use the colon syntax here too. To distinguish it from function
annotations, we could use a double colon (double colon is an
annotation with non-void semantics; quite a simple rule); or to
maintain an historic link with the existing packing/unpacking syntax,
we could look at an augmented form of the asteriks notation.

For instance:

def func(list*args, dict*kwargs) <- list-of-args, dict-of-kwargs
def func(args::list, kwargs::dict) <- I like the readability of this
one even better; args-list and kwargs-dict

And:

head, deque*tail = somedeque
head, tail::deque = somedeque

Or some variants thereof

Eelco

unread,
Dec 12, 2011, 2:20:51 PM12/12/11
to
As for calling functions; calling a function with the content of a
collection type rather than the collection as an object itself is a
rather weird special case operation I suppose, but we can cover it
with the same syntax:

def func(args::tuple, kwargs::dict):
funccall(args::, kwargs::) <- void type constraint means
unpacking, for symmetry with args/kwargs aggregation
funccall(::args, ::kwargs) <- I like this better, to emphasize it
being 'the other side' of the same coin, and quite close to ** syntax

Sequence and Mapping unpacking dont need their own symbols, if things
are done like this, since in the function declaration the meaning is
clear from the type of the annotations used, plus their position, and
in the call the meaning is clear from the type of the object
undergoing to unpacking operation.

OKB (not okblacke)

unread,
Dec 12, 2011, 2:45:10 PM12/12/11
to
Steven D'Aprano wrote:

> And you can blame C for the use of % instead of mod or modulo.

Anytime you can blame C for something, you can also blame a bunch
of other languages for foolishly perpetuating the inanities of C.

--
--OKB (not okblacke)
Brendan Barnwell
"Do not follow where the path may lead. Go, instead, where there is
no path, and leave a trail."
--author unknown

Terry Reedy

unread,
Dec 12, 2011, 4:58:33 PM12/12/11
to pytho...@python.org
On 12/12/2011 12:12 PM, Eelco wrote:

> Im not sure if this is a genuine understanding, or trollish
> obtuseness.

If you are referring to what I write, it is based on genuine
understanding of Python.

> Yes, the target can be anywhere in the sequence. And yes, the
> resulting list can contain objects of any type, so its very flexible
> in that regard too.
>
> But to relate it to the topic of this thread: no, the syntax does not
> allow one to select the type of the resulting sequence. It always
> constructs a list.

One use case of *target is to ignore the stuff collected in the target
because one only wants a few end values from the iterable. Another is to
pull stuff out because one wants to iterate through the rest. For both
uses, a list is as good as anything.

> Yes, we can cast the list to be whatever we want on the next line,

Convert. For the very few cases one wants to do this, it is quite adequate.

> but the question is whether this language design can be improved upon.

Not easily.

> The choice of a list feels arbitrary,

On the contrary, a list is precisely what is needed to collect an
indefinite number of leftovers.

> adding another line to cast it to
> something else would be even more verbose, and whats more, there would
> be serious performance implications if one should seek to apply this
> pattern to a deque/linkedlist; it would make taking off the head/tail
> of the list from a constant to a linear operation.

For a linked list, no *target and no copying is needed:

head, tail = llist

>>>> head, deque(tail) = somedeque
>
> Is better in every way I can think of (readability, consistence,
> performance) than:

>>>> head, *tail = somedeque
>>>> tail = deque(tail)

But your suggestion is much worse in each way than

head = somedeque.popleft()

To repeat, there is no reason to copy even once. If one does not want to
mutate the deque, then one mutates an iterator view of the deque.

--
Terry Jan Reedy

alex23

unread,
Dec 12, 2011, 6:54:09 PM12/12/11
to
On Dec 12, 10:21 pm, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> >  Modulo is hardly an obscure operation. "What's the remainder...?" is a
> >  simple question that people learn about in primary school.
>
> So is 'how much wood would a woodchucker chuck if a woodchucker could
> chuck wood?'. But how often does that concept turn up in your code?

That comment right there? That's the moment every serious coder
stopped paying attention to a single word you say.

Eelco

unread,
Dec 12, 2011, 6:40:38 PM12/12/11
to
> > Im not sure if this is a genuine understanding, or trollish
> > obtuseness.
>
> If you are referring to what I write, it is based on genuine
> understanding of Python.

This is getting 'interesting'. In a way. I meant to write
'misunderstanding', as I think the context made quite clear. So again
this adds another layer of doubt as to whether you are being obtuse
for its own sake, or if there is yet another layer of misunderstanding
stacked on top of the others. Either way, lets continue with the
benefit of the doubt.

> One use case of *target is to ignore the stuff collected in the target
> because one only wants a few end values from the iterable. Another is to
> pull stuff out because one wants to iterate through the rest. For both
> uses, a list is as good as anything.

So what is the point of having different sequence types, if a list can
do anything? I would argue that the different performance
characteristics and differences in semantics give each sequence type
ample reason for existence.

> Convert. For the very few cases one wants to do this, it is quite adequate.

Or so you opine. As you may recall, the original idea was motivated by
args/kwargs syntactic clarity and flexibility; I merely shifted focus
to this use case of collection unpacking since it is somewhat simpler
and easier to discuss. Keep that in mind, should you seek to build a
case for that assertion in the future.

>  > but the question is whether this language design can be improved upon.
>
> Not easily.

Again, so you opine. Care to give a refutation of my proposed double
colon syntax? Ignoring for a moment whether or not it is useful; does
it clash with anything? I havnt been able to think of anything in the
past few hours, but im not taking that to mean much; there are
probably some subtleties I am overlooking. I think it dos not clash
with slicing syntax for instance, but im not sure.

> For a linked list, no *target and no copying is needed:
>
> head, tail = llist

I have no idea what this means.

> >>>> head, deque(tail) = somedeque
>
> > Is better in every way I can think of (readability, consistence,
> > performance) than:
> >>>> head, *tail = somedeque
> >>>> tail = deque(tail)
>
> But your suggestion is much worse in each way than
>
> head = somedeque.popleft()

No its not. First of all, its not semantically equivalent; popleft
mutates a collection type, and collection unpacking does not; it
creates a set of disjoint views of the collection's data. Whether its
worse in terms of readability is debatable, but in terms of the other
two stated metrics, your claim of it being any kind of worse is
objectively false.

Furthermore, this brings us back again to the point I raised several
times before. Yes, obviously you can already DO these things, but NOT
within the same uniform collection unpacking syntactic construct.
Again, you have failed to point out what is wrong with supplying a
type constrain to python and let it do the right thing based on that;
to reiterate:

head, tail::deque = deque

No need to mutate anything; python can create the view of the
linkedlist internally. A single unambigious syntax, and single
unambigious semantics, for all sequence types. Whats not to like?

If you dont like the extra characters you have to type; there is of
course such a thing as defaults. You can choose:
head, tail:: = deque; if the type constraint is omitted, we could make
tail a list by default, or my preferred solution, infer it from the
right hand side type. In case of the former, all you had to do was
type :: instead of *, and your python universe would otherwise be
completely unchanged. If thats 'not easily', I dont know what is.

> To repeat, there is no reason to copy even once. If one does not want to
> mutate the deque, then one mutates an iterator view of the deque.

And arrive at yet another construction to do the same thing.

alex23

unread,
Dec 12, 2011, 7:27:45 PM12/12/11
to
On Dec 13, 3:12 am, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> But to relate it to the topic of this thread: no, the syntax does not
> allow one to select the type of the resulting sequence. It always
> constructs a list.

So by this argument, _every_ function that returns a list should take
an optional argument to specify an alternative form of sequence.

What, exactly, is so onerous about coercing your list to _whatever_
type you want? You know, like everybody else has been.

What does this _gain_ you other than one less line of code?


Ian Kelly

unread,
Dec 12, 2011, 7:34:25 PM12/12/11
to Eelco, pytho...@python.org
On Mon, Dec 12, 2011 at 11:51 AM, Eelco <hoogendo...@gmail.com> wrote:
> Either way, its not hard to add some detail to the semantics to allow
> all this. Even this function definition:
>
> def func(Foo(args), Foo(kwargs))
>
> ...could even be defined unambigiously by overloading first on base
> type, and if that does not uniquely determine the args and kwargs,
> fall back on positionality, so that:
>
> def func(Foo(args), dict(kwargs))
> def func(list(args), Foo(kwargs))
>
> would be uniquely defined as well.

That solves some of the problems, but if I just have:

def func(SequenceOrMappingType(args)):

That's going to unpack positionally. If I want it to unpack keywords
instead, how would I change the definition to indicate that?

Steven D'Aprano

unread,
Dec 12, 2011, 8:34:37 PM12/12/11
to
On Mon, 12 Dec 2011 16:58:33 -0500, Terry Reedy wrote:

> On 12/12/2011 12:12 PM, Eelco wrote:

>> Yes, we can cast the list to be whatever we want on the next line,
>
> Convert. For the very few cases one wants to do this, it is quite
> adequate.

+1

with the small proviso that there's not much you can usefully do with a
dict. If you convert to an ordered dict, the initial order is lost by the
time you see it; if you want to allow duplicates, duplicates are likewise
lost (or in a function call, an exception is raised).

In Python 4000, I'd like to see some way to pass arbitrary parameters to
functions, including duplicates. That is, emulating the richness of
command line argument handling in functions.

In Python 3.x, I'd like to see OrderedDict become a built-in, and then
**kwargs can collect named arguments in an ordered dict without losing
the order.



--
Steven

Ian Kelly

unread,
Dec 12, 2011, 8:41:34 PM12/12/11
to Eelco, pytho...@python.org
On Mon, Dec 12, 2011 at 4:40 PM, Eelco <hoogendo...@gmail.com> wrote:
>> For a linked list, no *target and no copying is needed:
>>
>> head, tail = llist
>
> I have no idea what this means.

Each node of a linked list consists of a data member and a "next"
member, that holds the next node in the list. The natural container
for this and the way it is usually implemented in Python is a
2-element tuple. For example:

llist = ('one', ('two', ('three', ('four', None))))

would be a linked list as typically constructed in Python, and it can
be used in pretty much the same way that lists are used in Lisp. The
result of the operation:

head, tail = llist

is:

head = 'one'
tail = ('two', ('three', ('four', None)))

and as Terry pointed out, no copying is required to do this. I
believe deque is also implemented as a doubly-linked list, which is
probably why you keep referring to it as such, but that is an
implementation detail. When you say "linked list" in relation to
Python, the preceding is what comes to mind.

>> >>>> head, deque(tail) = somedeque
>>
>> > Is better in every way I can think of (readability, consistence,
>> > performance) than:
>> >>>> head, *tail = somedeque
>> >>>> tail = deque(tail)
>>
>> But your suggestion is much worse in each way than
>>
>> head = somedeque.popleft()
>
> No its not. First of all, its not semantically equivalent; popleft
> mutates a collection type, and collection unpacking does not; it
> creates a set of disjoint views of the collection's data. Whether its
> worse in terms of readability is debatable, but in terms of the other
> two stated metrics, your claim of it being any kind of worse is
> objectively false.

I definitely disagree on readability. Skimming this thread as I have
been, it took me a while to get that your proposed syntax would create
a second deque sharing internal state with the original, rather than
creating a simple copy of the deque, which is what it looks like it
should do.

Incidentally, while that change is not really central to your syntax
proposal, I think it would be very messy. For example, how do you
propose keeping the length elements in sync? Inserting an item in one
may or may not affect the length of the other. If I append an item to
the end of one deque, should the other automatically be extended as
well? What if the tail node of the second deque occurs after the tail
node of the deque being appended? Does the appended element then get
inserted into the middle of the second deque (I think it would have to
be)? If I insert an element into the longer (second) deque that just
happens to be immediately after the tail of the shorter deque, does
*that* cause the shorter deque to be automatically extended? And
likewise for operations at the head of the deque.

None of these questions have obvious answers as to the "right" way to
it, and for that reason I think this is probably best left to the user
to implement a custom deque view class with whatever semantics they
prefer.

> Furthermore, this brings us back again to the point I raised several
> times before. Yes, obviously you can already DO these things, but NOT
> within the same uniform collection unpacking syntactic construct.
> Again, you have failed to point out what is wrong with supplying a
> type constrain to python and let it do the right thing based on that;
> to reiterate:
>
> head, tail::deque = deque

Python users generally follow the rule "explicit is better than
implicit". Setting a general constraint and letting the language "do
the right thing" is a kind of black magic that feels off because it
tends to break that rule. But that's not to say that black magic
never wins -- just look at super() and the MRO.

> If you dont like the extra characters you have to type; there is of
> course such a thing as defaults. You can choose:
> head, tail:: = deque; if the type constraint is omitted, we could make
> tail a list by default, or my preferred solution, infer it from the
> right hand side type.

I prefer the former. If it always creates a list by default, then the
programmer reading this code three years later knows exactly what the
type of tail is. If it instead infers it from the sequence being
unpacked, then the programmer is forced to infer it as well, and it
won't necessarily be obvious or even consistent.

Cheers,
Ian

Steven D'Aprano

unread,
Dec 12, 2011, 9:43:00 PM12/12/11
to
On Mon, 12 Dec 2011 04:21:15 -0800, Eelco wrote:

>> No more, or less, explicit than the difference between "==" and "is".
>
> == may be taken to mean identity comparison; 'equals' can only mean one
> thing.

Nonsense. "Equals" can be taken to mean anything the language designer
chooses, same as "==". There is no language police that enforces The One
True Meaning Of Equals. In fact, there is no one true meaning of equals.
Even my tiny Pocket Oxford dictionary lists five definitions.

It is ironic that the example you give, that of identity, is the standard
definition of equals in mathematics. 2*2 = 4 does not merely say that
"there is a thing, 2*2, which has the same value as a different thing,
4", but that both sides are the same thing. Two times two *is* four. All
numbers are, in some sense, singletons and equality implies identity.

A language designer might choose to define equals as an identity test, or
as a looser "values are the same" test where the value of an object or
variable is context dependent, *regardless* of how they are spelled: = ==
=== "is" "equals" or even "flibbertigibbet" if they wanted to be
whimsical. The design might allow types to define their own sense of
equality.

Triangle.equals(other_triangle) might be defined to treat any two
congruent triangles as equal; set equality could be defined as an
isomorphism relation; string equality could be defined as case-
insensitive, or to ignore leading and trailing whitespace. Regardless of
whether you or I *would* make those choices, we *could* make those
choices regardless of how our language spells the equality test.


> Of course 'formally' these symbols are well defined, but so is
> brainf*ck

I don't understand your point here.



>> Modulo is hardly an obscure operation. "What's the remainder...?" is a
>> simple question that people learn about in primary school.
>
>
> So is 'how much wood would a woodchucker chuck if a woodchucker could
> chuck wood?'. But how often does that concept turn up in your code?

You didn't make a statement about how often modulo turns up in code
(which is actually quite frequently, and possibly more frequently than
regular division), but about the obscurity of the operation. Taking the
remainder is not an obscure operation. The names "modulo" and "modulus"
may be obscure to those who haven't done a lot of mathematics, but the
concept of remainder is not. "How many pieces are left over after
dividing into equal portions" is something which even small children get.


>> And you can blame C for the use of % instead of mod or modulo.
>
> I didnt know one of Python's design goals was backwards compatibility
> with C.

Don't be silly. You know full well Python is not backwards compatible
with C, even if they do share some syntactical features.

C is merely one of many languages which have influenced Python, as are
Haskell, ABC, Pascal, Algol 68, Perl (mostly in the sense of "what not to
do" <wink>), Lisp, and probably many others. It merely happens that C's
use of the notation % for the remainder operation likely influenced
Python's choice of the same notation.

I note that the *semantics* of the operation differs in the two
languages, as I understand that the behaviour of % with negative
arguments is left undefined by the C standard, while Python does specify
the behaviour.


>> I can't imagine what sort of Python code you have seen that you
>> consider 90% attribute access "typical". I've just run the Python
>> tokenizer over my startup.py file, and I get these results:
>
> Yes, that was a hyperbole; but quite an often used construct, is it not?

It's hard, but not quite impossible, to write useful Python code without
it, so yes.


>> If you can supply any function at all, what happens if I write this:
>
>
> You cannot; only constructors modelling a sequence or a dict, and only
> in that order. Is that rule clear enough?

But why limit yourself to those restrictive rules?

If I want to collect a sequence of arguments into a string, why shouldn't
I be allowed to write this?

def func(parg, str(args)): ...

If I want to sum a collection of arguments, why not write this?

def func(pargs, sum(args)): ...

Isn't that better than this?

def func(pargs, *args):
args = sum(args)
...


But no. I don't mean those examples to be taken seriously: when you
answer to your own satisfaction why they are bad ideas, you may be closer
to understanding why I believe your idea is also a bad idea.



>> I believe that your proposal leads to an over-generalisation "call
>> arbitrary functions when handling parameter lists".
>
> I hope the above clears that up. It is as much about calling functions
> as ** is about raising kwargs to the power of.

I don't understand this comment. Nobody has suggested that ** in function
parameter lists is the exponentiation operator.

As for "calling functions", how else do you expect to generate a type if
you don't call the type constructor? One of your early examples was
something like:

def func(parg, attrdict(kwargs)): ...

If you expect kwargs to be an attrdict, which is not a built-in,
presumably you will have needed to have defined attrdict as a type or
function, and this type or function will get called at run time to
collect the kwargs. That is all.


>> I don't believe you
>> need this added complication. If you want to your var args as a list,
>> call list(args) inside your function.
>
> We dont strictly 'need' any language construct. Real men use assembler,
> right?

"We're not using assembly" is not a reason to add a feature to a
language. Every feature adds cost to the language:

* harder to implement;
* harder to maintainer;
* larger code base;
* more documentation to be written;
* more tests to be written;
* more for users to learn

etc.


>> > head, tuple(tail) = iterable
>> In Python 3, that is spelled:
>> head, *tail = iterable
>> tail = tuple(tail)
>
> Yes, I know. How is that not a lot more verbose and worse than what I
> have proposed in all possible ways?

That *specific* syntax, outside of function declarations, is something
I've often thought might be useful. But if I were to argue its case, I
would allow arbitrary functions, and treat it as syntactic sugar for:

head, *tail = iterable
tail = func(tail) # or possibly func(*tail)

But that's pie-in-the-sky musing. I certainly wouldn't put it in function
parameter lists. Param lists should be declarations, not code. Apart from
the most limited collation of args, code belongs inside the body of the
function, not the param list:

def foo(a, 2*b+1, c): # double the second arg and add 1


>> head, tail = somestring[0], somestring[1:]
>
> Well yes, splendid; we can do that with lists too since the dawn of
> Python. What you are saying here in effect is that you think the
> head/tail syntax is superfluous; that youd rather see it eliminated than
> generalized.

No.

It is not "head/tail" syntax, but sequence unpacking, and it has been
nicely generalised to allow things like this in Python 3:

a, b, c, d, *lump, x, y z = any_iterable

any_iterable isn't limited to a list, str, or other object which supports
slicing. It can be any object supporting the iteration protocol.

What I'm saying is that there is no need to OVER-generalise this to
specify the type of *lump within the packing operation. If you want lump
to be something other that Python's choice, perform the conversion
yourself.


--
Steven

Chris Angelico

unread,
Dec 12, 2011, 10:08:28 PM12/12/11
to pytho...@python.org
On Tue, Dec 13, 2011 at 1:43 PM, Steven D'Aprano
<steve+comp....@pearwood.info> wrote:
> It merely happens that C's
> use of the notation % for the remainder operation likely influenced
> Python's choice of the same notation.

Considering that Python also had the notion that "integer divided by
integer yields integer" until Py3, I would say it's extremely likely
that most of Python's division facilities were modelled off C. That's
not a bad thing; gives you a set of operations that a large number of
people will grok, and only a small number of oddities.

> I note that the *semantics* of the operation differs in the two
> languages, as I understand that the behaviour of % with negative
> arguments is left undefined by the C standard, while Python does specify
> the behaviour.

... and there's the proof that "modelled off" does not mean "slavishly
follows". This lack of definition is a weakness in C.

> def foo(a, 2*b+1, c):  # double the second arg and add 1

No, that should subtract 1 from the second arg and halve it. The
expression you give there has to match the value from the parameter
list.

This syntax would be a huge boon to Python. Imagine how much easier
this could make things:

def foo(sum(x)):
return x

print(foo(120)) # will print a list of numbers that sum to 120


ChrisA

Ian Kelly

unread,
Dec 12, 2011, 11:52:11 PM12/12/11
to Steven D'Aprano, pytho...@python.org
On Mon, Dec 12, 2011 at 7:43 PM, Steven D'Aprano
<steve+comp....@pearwood.info> wrote:
> If I want to collect a sequence of arguments into a string, why shouldn't
> I be allowed to write this?
>
>    def func(parg, str(args)): ...

Obviously, because the correct syntax would be:

def func(parg, ''.join(args)): ...

:-P

Eelco

unread,
Dec 13, 2011, 3:30:06 AM12/13/11
to
1) Turning two lines of code into a single more readable one is
nothing to scoff at
2) After-the-fact conversion is O(n), getting the view you want right
away is O(1)

Not every function needs this flexibility; many specialized functions
could not care less. But collection unpacking is quite a general
thing, and for the record; slicing a tuple returns a tuple. Would you
rather have that return a list too?

Eelco

unread,
Dec 13, 2011, 3:31:21 AM12/13/11
to
On Dec 13, 1:34 am, Ian Kelly <ian.g.ke...@gmail.com> wrote:
That should raise an ambiguity error. But honestly, how often have you
worked with SequenceOrMappingType's? I think this is a rather
palatable constraint.

Eelco

unread,
Dec 13, 2011, 3:44:20 AM12/13/11
to
On Dec 13, 2:41 am, Ian Kelly <ian.g.ke...@gmail.com> wrote:
> On Mon, Dec 12, 2011 at 4:40 PM, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> >> For a linked list, no *target and no copying is needed:
>
> >> head, tail = llist
>
> > I have no idea what this means.
>
> Each node of a linked list consists of a data member and a "next"
> member, that holds the next node in the list.  The natural container
> for this and the way it is usually implemented in Python is a
> 2-element tuple.  For example:
>
> llist = ('one', ('two', ('three', ('four', None))))
>
> would be a linked list as typically constructed in Python, and it can
> be used in pretty much the same way that lists are used in Lisp.  The
> result of the operation:
>
> head, tail = llist
>
> is:
>
> head = 'one'
> tail = ('two', ('three', ('four', None)))
>
> and as Terry pointed out, no copying is required to do this.  I
> believe deque is also implemented as a doubly-linked list, which is
> probably why you keep referring to it as such, but that is an
> implementation detail.  When you say "linked list" in relation to
> Python, the preceding is what comes to mind.

'for i in llist' is not quite going to fly is it? Thats probably the
reason noone ever uses that construct; its not a proper sequence type.


> >> >>>> head, deque(tail) = somedeque
>
> >> > Is better in every way I can think of (readability, consistence,
> >> > performance) than:
> >> >>>> head, *tail = somedeque
> >> >>>> tail = deque(tail)
>
> >> But your suggestion is much worse in each way than
>
> >> head = somedeque.popleft()
>
> > No its not. First of all, its not semantically equivalent; popleft
> > mutates a collection type, and collection unpacking does not; it
> > creates a set of disjoint views of the collection's data. Whether its
> > worse in terms of readability is debatable, but in terms of the other
> > two stated metrics, your claim of it being any kind of worse is
> > objectively false.
>
> I definitely disagree on readability.  Skimming this thread as I have
> been, it took me a while to get that your proposed syntax would create
> a second deque sharing internal state with the original, rather than
> creating a simple copy of the deque, which is what it looks like it
> should do.

Thats a matter of reading up on the semantics of collection unpacking.
To be honest I dont even know what they are for lists, since I dont
use python 3. But whatever is the case, the important thing is that
the semantics should be uniform regardless of the type to be unpacked.

> Incidentally, while that change is not really central to your syntax
> proposal, I think it would be very messy.  For example, how do you
> propose keeping the length elements in sync?  Inserting an item in one
> may or may not affect the length of the other.  If I append an item to
> the end of one deque, should the other automatically be extended as
> well?  What if the tail node of the second deque occurs after the tail
> node of the deque being appended?  Does the appended element then get
> inserted into the middle of the second deque (I think it would have to
> be)?  If I insert an element into the longer (second) deque that just
> happens to be immediately after the tail of the shorter deque, does
> *that* cause the shorter deque to be automatically extended?  And
> likewise for operations at the head of the deque.
>
> None of these questions have obvious answers as to the "right" way to
> it, and for that reason I think this is probably best left to the user
> to implement a custom deque view class with whatever semantics they
> prefer.

Good point. Copy-on-write semantics could be used, but really one
should have several linked list types reflecting the underlying
implementations. I would like to have an immutable singly linked list
builtin of the standard functional type, which you can only unpack
from one end and renders these issues moot, plus a builtin doubly
linked list with copy-on-write or copy-on-unpacking semantics.

> > Furthermore, this brings us back again to the point I raised several
> > times before. Yes, obviously you can already DO these things, but NOT
> > within the same uniform collection unpacking syntactic construct.
> > Again, you have failed to point out what is wrong with supplying a
> > type constrain to python and let it do the right thing based on that;
> > to reiterate:
>
> > head, tail::deque = deque
>
> Python users generally follow the rule "explicit is better than
> implicit".  Setting a general constraint and letting the language "do
> the right thing" is a kind of black magic that feels off because it
> tends to break that rule.  But that's not to say that black magic
> never wins -- just look at super() and the MRO.

We are not talking black magic here; we are talking about an EXPLICIT
type constraint provided on the very same line.

> > If you dont like the extra characters you have to type; there is of
> > course such a thing as defaults. You can choose:
> > head, tail:: = deque; if the type constraint is omitted, we could make
> > tail a list by default, or my preferred solution, infer it from the
> > right hand side type.
>
> I prefer the former.  If it always creates a list by default, then the
> programmer reading this code three years later knows exactly what the
> type of tail is.  If it instead infers it from the sequence being
> unpacked, then the programmer is forced to infer it as well, and it
> won't necessarily be obvious or even consistent.

Well perhaps, but not always knowing the type of your objects at write-
time is inherent to weakly typed languages; this happens all the time.
Not knowing the type of the sequence to be unpacked is in a sense an
asset; I can use this construct in a function, and unpack any sequence
type in a manner appropriate for it. About the result of the unpacking
I will know just as much as about the input to it; that they are the
same type.

Eelco

unread,
Dec 13, 2011, 4:15:46 AM12/13/11
to
On Dec 13, 3:43 am, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> On Mon, 12 Dec 2011 04:21:15 -0800, Eelco wrote:
> >> No more, or less, explicit than the difference between "==" and "is".
>
> > == may be taken to mean identity comparison; 'equals' can only mean one
> > thing.
>
> Nonsense. "Equals" can be taken to mean anything the language designer
> chooses, same as "==". There is no language police that enforces The One
> True Meaning Of Equals. In fact, there is no one true meaning of equals.
> Even my tiny Pocket Oxford dictionary lists five definitions.
>
> It is ironic that the example you give, that of identity, is the standard
> definition of equals in mathematics. 2*2 = 4 does not merely say that
> "there is a thing, 2*2, which has the same value as a different thing,
> 4", but that both sides are the same thing. Two times two *is* four. All
> numbers are, in some sense, singletons and equality implies identity.

We are not talking mathemathics, we are talking programming languages.
Identity versus value equality is a well established concept within
its jargon. Within this context, 'equals' and 'is' have clearly
defined meanings. Indeed within broader use, including everyday
language, the distinction is more subtle and therefore less useful,
but obeying a linguistic rule that holds within computer science is
better than making something up just for python, even though its not
the yet better rule that any five year old might grasp.

> > Of course 'formally' these symbols are well defined, but so is
> > brainf*ck
>
> I don't understand your point here.

Hence the above.

> >>  Modulo is hardly an obscure operation. "What's the remainder...?" is a
> >>  simple question that people learn about in primary school.
>
> > So is 'how much wood would a woodchucker chuck if a woodchucker could
> > chuck wood?'. But how often does that concept turn up in your code?
>
> You didn't make a statement about how often modulo turns up in code
> (which is actually quite frequently, and possibly more frequently than
> regular division), but about the obscurity of the operation. Taking the
> remainder is not an obscure operation. The names "modulo" and "modulus"
> may be obscure to those who haven't done a lot of mathematics, but the
> concept of remainder is not. "How many pieces are left over after
> dividing into equal portions" is something which even small children get.

So 'frequency of use' is no valid interpretation of 'obscurity'? Im
not a native speaker, but im pretty sure it is.

> >> And you can blame C for the use of % instead of mod or modulo.
>
> > I didnt know one of Python's design goals was backwards compatibility
> > with C.
>
> Don't be silly. You know full well Python is not backwards compatible
> with C, even if they do share some syntactical features.

Of course I was being silly; I know this use is following a historical
precedent; but id rather see it rectified in the previous version of
python rather than the next. My sillyness was prompted by the
percieved pointlessness of your remark. Of course python is not trying
to be backwards compatible with C; so why bring it up then?

> >>  If you can supply any function at all, what happens if I write this:
>
> > You cannot; only constructors modelling a sequence or a dict, and only
> > in that order. Is that rule clear enough?
>
> But why limit yourself to those restrictive rules?
>
> If I want to collect a sequence of arguments into a string, why shouldn't
> I be allowed to write this?
>
>     def func(parg, str(args)): ...
>
> If I want to sum a collection of arguments, why not write this?
>
>     def func(pargs, sum(args)): ...
>
> Isn't that better than this?
>
>     def func(pargs, *args):
>         args = sum(args)
>         ...
>
> But no. I don't mean those examples to be taken seriously: when you
> answer to your own satisfaction why they are bad ideas, you may be closer
> to understanding why I believe your idea is also a bad idea.

They are bad ideas because they truely do not lead to the execution of
different code, but are merely a reordering, mixing statements in with
a function declaration. I am proposing no such thing; again, the
type(arg) notation I have dropped, and never was meant to have
anything to do with function calling; it is a way of supplying an
optional type constraint, so in analogy with function annotations, I
changed that to arg::type. Again, this has nothing to do with calling
functions on arguments.

First off, type constraints must have some use; all those languages
cant be wrong. Ofcourse, no type constraints also can not be wrong;
see all those other languages. And I obviously dont mean to make type
constraits mandatory in python, all im saying is that optionally
allowing them can open some doors in the right places. The * and **
syntax are also in effect type constraints, saying 'this is a dict to
collect the remaining kwargs in', but in a rather cryptic and
needlessly ungeneral method.

#define a function with args-list and kwargs-attrdict
def func(args::list, kwargs::attrdict)
#unpack the sequence args into positional arguments, and the mapping
kwargs into keyword arguments
func(::args, ::kwargs)

So now that I have explained what my idea actually is, perhaps youd
like to explain again why it is a bad idea?

> >> I believe that your proposal leads to an over-generalisation "call
> >> arbitrary functions when handling parameter lists".
>
> > I hope the above clears that up. It is as much about calling functions
> > as ** is about raising kwargs to the power of.
>
> I don't understand this comment. Nobody has suggested that ** in function
> parameter lists is the exponentiation operator.

No more or less than I suggested this is about calling functions on
input parameters. The syntax might be superficially similar, but thats
all.

> As for "calling functions", how else do you expect to generate a type if
> you don't call the type constructor? One of your early examples was
> something like:
>
> def func(parg, attrdict(kwargs)): ...
>
> If you expect kwargs to be an attrdict, which is not a built-in,
> presumably you will have needed to have defined attrdict as a type or
> function, and this type or function will get called at run time to
> collect the kwargs. That is all.

I would like attrdict to be a builtin, and as long as it isnt, that
specific example probably wouldnt work. But all I wnat python to do is
whatever it is doing to build a dict, but then for another type
instead. Hence, a typeconstraint.

>
> >>  >  head, tuple(tail) = iterable
> >>  In Python 3, that is spelled:
> >>  head, *tail = iterable
> >>  tail = tuple(tail)
>
> > Yes, I know. How is that not a lot more verbose and worse than what I
> > have proposed in all possible ways?
>
> That *specific* syntax, outside of function declarations, is something
> I've often thought might be useful. But if I were to argue its case, I
> would allow arbitrary functions, and treat it as syntactic sugar for:
>
> head, *tail = iterable
> tail = func(tail)  # or possibly func(*tail)
>
> But that's pie-in-the-sky musing. I certainly wouldn't put it in function
> parameter lists. Param lists should be declarations, not code. Apart from
> the most limited collation of args, code belongs inside the body of the
> function, not the param list:

I agree that this is a bad idea; see the above.

> >> head, tail = somestring[0], somestring[1:]
>
> > Well yes, splendid; we can do that with lists too since the dawn of
> > Python. What you are saying here in effect is that you think the
> > head/tail syntax is superfluous; that youd rather see it eliminated than
> > generalized.
>
> No.
>
> It is not "head/tail" syntax, but sequence unpacking, and it has been
> nicely generalised to allow things like this in Python 3:
>
> a, b, c, d, *lump, x, y z = any_iterable
>
> any_iterable isn't limited to a list, str, or other object which supports
> slicing. It can be any object supporting the iteration protocol.
>
> What I'm saying is that there is no need to OVER-generalise this to
> specify the type of *lump within the packing operation. If you want lump
> to be something other that Python's choice, perform the conversion
> yourself.

Oh, so your primary objection is a semantic nitpick, and the latter is
an OVERgeneralization? Why not restrict unpacking only to lists as
RHS, and convert all your iterables to lists first? Who needs this
overgeneralization of allowing any sequence type to be unpacked,
right?

To answer that question: for the same reasons. The conversion is
wasteful; allowing python to do the right thing based on a
typeconstraint is not. Plus, it is less code, and more readable code;
the only rule you have to learn is quite general, which is that :: is
a type constraint annotation; no need to remember specifics, like
'unpacking always returns lists for some arbitrary reason'.

Eelco

unread,
Dec 13, 2011, 4:26:02 AM12/13/11
to
> > Python users generally follow the rule "explicit is better than
> > implicit".  Setting a general constraint and letting the language "do
> > the right thing" is a kind of black magic that feels off because it
> > tends to break that rule.  But that's not to say that black magic
> > never wins -- just look at super() and the MRO.
>
> We are not talking black magic here; we are talking about an EXPLICIT
> type constraint provided on the very same line.

To hammer that point home: would you say the following C# code
performs 'black magic'?

float x = 3;

After all, exactly the same thing is going on; C# will 'do the right
thing'; convert the integer literal 3 to a floating point value, based
on the type constraint placed on x.

Eelco

unread,
Dec 13, 2011, 4:50:18 AM12/13/11
to
> To answer that question: for the same reasons. The conversion is
> wasteful; allowing python to do the right thing based on a
> typeconstraint is not. Plus, it is less code, and more readable code;
> the only rule you have to learn is quite general, which is that :: is
> a type constraint annotation; no need to remember specifics, like
> 'unpacking always returns lists for some arbitrary reason'.

Oh my bad; actually, that should be:

'collecting the remainder of an unpacked iterable using * will always
yield a list. That is, unless the construct appears inside a function
definition; then somehow a tuple is always the right choice'

Arnaud Delobelle

unread,
Dec 13, 2011, 5:15:33 AM12/13/11
to Eelco, pytho...@python.org
When you quote somebody (even yourself), it would be helpful if you
attributed your quote.

--
Arnaud

Eelco

unread,
Dec 13, 2011, 5:39:52 AM12/13/11
to
On 13 dec, 11:15, Arnaud Delobelle <arno...@gmail.com> wrote:
Ah yes; im more used to proper forums, still getting used to these
mailing-list things. But point taken.

Eelco

unread,
Dec 13, 2011, 5:46:13 AM12/13/11
to
With all this being said, I must say that the notion of indtroducing
type constraints into Python is quite a radical one*, and one that
should not be taken lightly, so I understand the general conservative
vibe the notion is getting. It probably has implications beyond just
collection types, and if youd introduce such a feature, you would like
to introduce it only once, and correctly the first time around.

Ill probably start a new thread soon, recapping the accumulated
insight, and capping all the OT threads that have spawned.

*even though the asteriks syntax is infact a limited form of exactly
that

Steven D'Aprano

unread,
Dec 13, 2011, 6:13:16 AM12/13/11
to
On Tue, 13 Dec 2011 01:15:46 -0800, Eelco wrote:

> On Dec 13, 3:43 am, Steven D'Aprano <steve
> +comp.lang.pyt...@pearwood.info> wrote:
>> On Mon, 12 Dec 2011 04:21:15 -0800, Eelco wrote:
>> >> No more, or less, explicit than the difference between "==" and
>> >> "is".
>>
>> > == may be taken to mean identity comparison; 'equals' can only mean
>> > one thing.
[...]
> We are not talking mathemathics, we are talking programming languages.

What *I* am talking about is your assertion that there is only one
possible meaning for "equals" in the context of a programing language.
This is *simply not correct*.

You don't have to believe me, just look at the facts. It's hard to find
languages that use the word "equals" (or very close to it) rather than
equals signs, but here are four languages which do:

(1) OpenXION:

Equals in OpenXION is weakly typed, like Perl:

>1 + 1.0 equals "2"
true


(2) C# uses method notation: a.Equals(b) can be overridden, but for many
types it defaults to value equality, that is, the equivalent to Python's
a == b.

(3) Ruby uses a.equal?(b) for "reference equality", that is, the
equivalent of Python's "is" operator:

irb(main):001:0> a = "abc"
=> "abc"
irb(main):002:0> b = "abc"
=> "abc"
irb(main):003:0> a.equal?(b)
=> false
irb(main):004:0> a == b
=> true


(4) Mathematica's Equal[x, y] can test values and expressions for
equality. It may return true, false, or unevaluated (i.e. itself).


Four languages that use Equals (or close to it) with four different
behaviours.



> Identity versus value equality is a well established concept within its
> jargon. Within this context, 'equals' and 'is' have clearly defined
> meanings.

Incorrect. Most programming languages do not even have a concept of
identity: identity is only(?) relevant to reference languages, like
Python, where variables are references to objects.

Even for languages that have a concept of identity, most don't don't call
it "is". Objective-C calls it "==", PHP calls it "===", C# calls it
object.ReferenceEquals. (Python, Algol 68, and VB .NET are three which do
call it "is".)

For stack-based languages like Forth, it doesn't even make sense to talk
about identity, since values aren't variables: they're just values on a
stack, not values in a fixed location, or bound to a known name.

Again, all this goes to demonstrate that the language designer is free to
choose any behaviour they like, and give it any name they like.


[...]
> So 'frequency of use' is no valid interpretation of 'obscurity'? Im not
> a native speaker, but im pretty sure it is.

No. Things which are obscure are used in language infrequently, because
if they were common they would not be obscure. But things which are used
infrequently are not necessarily obscure.

An example in common language: "Napoleon Bonaparte" does not come up in
conversation very frequently, but he is not an obscure historical figure.

An example from programming: very few people need to use the
trigonometric functions sin, cos, tan in their code. But they are not
obscure functions: most people remember them from school. People who have
forgotten almost everything about mathematics except basic arithmetic
probably remember sin, cos and tan. But they never use them.



>> >> And you can blame C for the use of % instead of mod or modulo.
>>
>> > I didnt know one of Python's design goals was backwards compatibility
>> > with C.
>>
>> Don't be silly. You know full well Python is not backwards compatible
>> with C, even if they do share some syntactical features.
>
> Of course I was being silly; I know this use is following a historical
> precedent; but id rather see it rectified in the previous version of
> python rather than the next. My sillyness was prompted by the percieved
> pointlessness of your remark. Of course python is not trying to be
> backwards compatible with C; so why bring it up then?

Because you asked why Python uses the % operator for remainder.


[...]
> They are bad ideas because they truely do not lead to the execution of
> different code, but are merely a reordering, mixing statements in with a
> function declaration. I am proposing no such thing; again, the type(arg)
> notation I have dropped, and never was meant to have anything to do with
> function calling; it is a way of supplying an optional type constraint,
> so in analogy with function annotations, I changed that to arg::type.
> Again, this has nothing to do with calling functions on arguments.

You have not thought about this carefully enough. Consider what happens
when this code gets called:

def f(*args): pass

f(a, b, c)


The Python virtual machine (interpreter, if you prefer) must take three
arguments a, b, c and create a tuple from them. This must happen at
runtime, because the value of the objects is not known at compile time.
So at some point between f(a, b, c) being called and the body of f being
entered, a tuple must be created, and the values of a, b, c must be
collated into a single tuple.

Now extend this reasoning to your proposal:

def f(args:FOO): pass

At runtime, the supplied arguments must be collated into a FOO, whatever
FOO happens to be. Hence, the function that creates FOO objects must be
called before the body of f can be entered. This doesn't happen for free.
Whether you do it manually, or have the Python interpreter do it, it
still needs to be done.


> First off, type constraints must have some use; all those languages cant
> be wrong.

But you're not talking about type constraints. You're not instructing the
function to reject arguments which have the wrong type, you are
instructing it to collate multiple arguments into a list (instead of a
tuple like Python currently does). def f(*args) *constructs* a tuple, it
doesn't perform a type-check.




--
Steven

Steven D'Aprano

unread,
Dec 13, 2011, 6:28:23 AM12/13/11
to
On Tue, 13 Dec 2011 02:46:13 -0800, Eelco wrote:

> With all this being said, I must say that the notion of indtroducing
> type constraints into Python is quite a radical one*,

Not that radical. Here's the creator of Python musing about adding
optional type checks to Python:

http://www.artima.com/weblogs/viewpost.jsp?thread=85551
http://www.artima.com/weblogs/viewpost.jsp?thread=86641
http://www.artima.com/weblogs/viewpost.jsp?thread=87182


[...]
> *even though the asteriks syntax is infact a limited form of exactly
> that

It absolutely is not. def f(*args, **kwargs) constructs a tuple and a
dict, it does not type-check that the function is passed a tuple and a
dict as arguments. These are completely different things.


--
Steven

Eelco

unread,
Dec 13, 2011, 7:19:26 AM12/13/11
to
On 13 dec, 12:28, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> On Tue, 13 Dec 2011 02:46:13 -0800, Eelco wrote:
> > With all this being said, I must say that the notion of indtroducing
> > type constraints into Python is quite a radical one*,
>
> Not that radical. Here's the creator of Python musing about adding
> optional type checks to Python:
>
> http://www.artima.com/weblogs/viewpost.jsp?thread=85551http://www.artima.com/weblogs/viewpost.jsp?thread=86641http://www.artima.com/weblogs/viewpost.jsp?thread=87182

Good find; but still radical enough that it hasnt been implemented.
Note that these musing are trying to adress a yet far more general
problem of specifying arbitrary types constraints on anything; I am
primarily interested in specifying container types in the special case
of collection packing/unpacking syntax, with further extensions
nothing but a welcome addon. The fact that the former was judged
infeasible does not mean the more modest goal of the latter might not
be attainable.


> > *even though the asteriks syntax is infact a limited form of exactly
> > that
>
> It absolutely is not. def f(*args, **kwargs) constructs a tuple and a
> dict, it does not type-check that the function is passed a tuple and a
> dict as arguments. These are completely different things.

Which is of course not something I ever proposed; I never said
anything about checking types of existing data; im talking about
coercing types of newly created data, like the target of a collection
packing. That is exactly what *args and **kwargs also do.

Eelco

unread,
Dec 13, 2011, 7:47:58 AM12/13/11
to
On 13 dec, 12:13, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> On Tue, 13 Dec 2011 01:15:46 -0800, Eelco wrote:
> > On Dec 13, 3:43 am, Steven D'Aprano <steve
> > +comp.lang.pyt...@pearwood.info> wrote:
> >> On Mon, 12 Dec 2011 04:21:15 -0800, Eelco wrote:
> >> >> No more, or less, explicit than the difference between "==" and
> >> >> "is".
>
> >> > == may be taken to mean identity comparison; 'equals' can only mean
> >> > one thing.
> [...]
> > We are not talking mathemathics, we are talking programming languages.
>
> What *I* am talking about is your assertion that there is only one
> possible meaning for "equals" in the context of a programing language.
> This is *simply not correct*.
>
> You don't have to believe me, just look at the facts. It's hard to find
> languages that use the word "equals" (or very close to it) rather than
> equals signs, but here are four languages which do:

That python is not the only language to not get this quite as right as
could be is known to me. But within computer science as a discipline,
equality and identity comparisons have a clear enough meaning. 'is'
has a narrower meaning than 'equals'. '==' has no meaning whatsoever
in computer science.

> Again, all this goes to demonstrate that the language designer is free to
> choose any behaviour they like, and give it any name they like.

Certainly you demonstrated as much. Programming languages are created
by people, and they have a tendency to make mistakes, and to have to
compromise (backwards compatibility, and so on). Thats a seperate
matter from 'what ought to be done', when discussing optimal language
design.

> > So 'frequency of use' is no valid interpretation of 'obscurity'? Im not
> > a native speaker, but im pretty sure it is.
>
> No. Things which are obscure are used in language infrequently, because
> if they were common they would not be obscure. But things which are used
> infrequently are not necessarily obscure.
>
> An example in common language: "Napoleon Bonaparte" does not come up in
> conversation very frequently, but he is not an obscure historical figure.
>
> An example from programming: very few people need to use the
> trigonometric functions sin, cos, tan in their code. But they are not
> obscure functions: most people remember them from school. People who have
> forgotten almost everything about mathematics except basic arithmetic
> probably remember sin, cos and tan. But they never use them.

I dont think its terribly interesting to debate whether the term
obscure applies to trigonometric functions or not: the important
matter is that they are where they should be; under math.cos, etc.
They dont have their own special character, and I hope you agree that
is as it should be.

I use trig far more often than modulus, so that argues in favor of
modulus being under math too; infact I used modulus quite recently,
but naturally it was in a piece of code that should be done in C
eventually anyway (evaluating subdivision surfaces)

> Because you asked why Python uses the % operator for remainder.

So you ARE implying python has backwards compatibility with C as a
design goal? Otherwise the given answer to this question is
nonsensical.
Of course the python interpreted needs to do this; and in case non-
builtin types are allowed, the mechanism is going to be through their
constructor. But thats a detail; the syntax doesnt say: 'please call
this constructor for me', any more than **kwargs says 'please call a
dict constructor for me', even though equivalent operations are
obviously going on under the hood as part of the process. Yes,
whatever type you have needs to be constructed, and its not magically
going to happen, its going to cost CPU cycles. The question is, do you
end up constructing both a tuple and a list, or do you construct the
list directly?


> > First off, type constraints must have some use; all those languages cant
> > be wrong.
>
> But you're not talking about type constraints. You're not instructing the
> function to reject arguments which have the wrong type, you are
> instructing it to collate multiple arguments into a list (instead of a
> tuple like Python currently does). def f(*args) *constructs* a tuple, it
> doesn't perform a type-check.

I am talking about type constraints, but as seems to be the usual
pattern in our miscommunications, you seek to attach a specific far
fetched meaning to it that I never intended. Insofar as I understand
these terms, a type-constraint is part of a declaration; float x; in C-
family languages, args::list in python 4 perhaps. It narrows down the
semantics for further usage of that symbol. A type-check is something
along the lines of type(args)==list, a runtime thing and something
completely different. I havnt mentioned the latter at all, explicitly
or implicitly, as far as im aware.

Chris Angelico

unread,
Dec 13, 2011, 8:14:33 AM12/13/11
to pytho...@python.org
On Tue, Dec 13, 2011 at 11:47 PM, Eelco <hoogendo...@gmail.com> wrote:
>> def f(*args) *constructs* a tuple, it
>> doesn't perform a type-check.
>
> I am talking about type constraints... A type-check is something
> along the lines of type(args)==list, a runtime thing and something
> completely different. I havnt mentioned the latter at all, explicitly
> or implicitly, as far as im aware.

I'm not sure what you mean by a "type constraint". Here's how I understand such:

float|int foobar; //Declare that the variable 'foobar' is allowed to
hold a float or an int
foobar = 3.2; //Legal.
foobar = 1<<200; //Also legal (a rather large integer value)
foobar = "hello"; //Not legal

Python doesn't have any such thing (at least, not in-built). Any name
may be bound to any value - or if you like, any variable can contain
any type. Same applies to argument passing - you can even take an
unbound method and call it with some completely different type of
object as its first parameter. (I can't think of ANY situation in
which this would not be insanely confusing, tbh.) When you gather a
function's arguments into a tuple, list, or any other such container
object, what you're doing is constructing another object and stuffing
it with references to the original arguments. That involves building a
new object, where otherwise you simply bind the arguments' names to
the original objects directly.

Now, it is perfectly conceivable to have designed Python to _always_
pass tuples around. Instead of a set of arguments, you just always
pass exactly one tuple to a function, and that function figures out
what to do with the args internally. If that's the way the language is
built, then it is the _caller_, not the callee, who builds that tuple;
and we still have the same consideration if we want a list instead.

So suppose you can have a user-defined object type instead of
list/dict. How are you going to write that type's __init__ function?
Somewhere along the way, you need to take a variable number of
arguments and bundle them up into a single one... so somewhere, you
need the interpreter to build it for you. This is going to end up
exactly the same as just accepting the tuple and then passing that to
a constructor, like the list example. Keep things transparent and you
make debugging a LOT easier.

ChrisA

Eelco

unread,
Dec 13, 2011, 8:35:27 AM12/13/11
to
On 13 dec, 14:14, Chris Angelico <ros...@gmail.com> wrote:
> On Tue, Dec 13, 2011 at 11:47 PM, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> >> def f(*args) *constructs* a tuple, it
> >> doesn't perform a type-check.
>
> > I am talking about type constraints... A type-check is something
> > along the lines of type(args)==list, a runtime thing and something
> > completely different. I havnt mentioned the latter at all, explicitly
> > or implicitly, as far as im aware.
>
> I'm not sure what you mean by a "type constraint". Here's how I understand such:
>
> float|int foobar; //Declare that the variable 'foobar' is allowed to
> hold a float or an int
> foobar = 3.2; //Legal.
> foobar = 1<<200; //Also legal (a rather large integer value)
> foobar = "hello"; //Not legal
>
> Python doesn't have any such thing (at least, not in-built).

Agreed on what a type constraint is, and that python does not really
have any, unless one counts the current use of asterikses, which are
infact a limited form of type constrait (*tail is not just any object,
but one holding a list, which modifies the semantics of the assignment
statement 'head,*tail=sequence' from a regular tuple unpacking to a
specific form of the more general collection unpacking syntax);

The idea is to enrich this syntax; to add optional and limited type
constraints to python, specifically to enrich collection packing/
unpacking syntax, but perhaps the concept can be further generalized.


> So suppose you can have a user-defined object type instead of
> list/dict. How are you going to write that type's __init__ function?
> Somewhere along the way, you need to take a variable number of
> arguments and bundle them up into a single one... so somewhere, you
> need the interpreter to build it for you. This is going to end up
> exactly the same as just accepting the tuple and then passing that to
> a constructor, like the list example. Keep things transparent and you
> make debugging a LOT easier.

Agreed; for user defined collection types there would not be a
performance benefit over converting a tuple, since this is exactly
what will happen anyway, but for collection types derived from any of
the builtins, python could optimize away the intermediate and
construct the desired collection type directly from the available
information.

Ian Kelly

unread,
Dec 13, 2011, 1:15:21 PM12/13/11
to Eelco, pytho...@python.org
On Tue, Dec 13, 2011 at 1:44 AM, Eelco <hoogendo...@gmail.com> wrote:
> 'for i in llist' is not quite going to fly is it? Thats probably the
> reason noone ever uses that construct; its not a proper sequence type.

Not really a problem, because fortunately Python makes it super-easy
to create custom iterators.

def listiter(llist):
while llist:
head, llist = llist
yield head

And you're done. If you like, you could also wrap this up in a class
with all the sequence-related magic methods you want, although you
lose the simplicity of the literal syntax by doing so. If this is
rarely used, it is more likely because custom containers are going to
be less efficient than built-ins, but if you want to do functional
programming and are not overly concerned with the speed of iteration,
this is not a bad way to do it.

> Good point. Copy-on-write semantics could be used, but really one
> should have several linked list types reflecting the underlying
> implementations. I would like to have an immutable singly linked list
> builtin of the standard functional type, which you can only unpack
> from one end and renders these issues moot, plus a builtin doubly
> linked list with copy-on-write or copy-on-unpacking semantics.

Copy-on-write could be implemented with any type. You don't need a
doubly linked list for that.

> We are not talking black magic here; we are talking about an EXPLICIT
> type constraint provided on the very same line.

An explicit type constraint with very different semantics depending on
what particular type you specify and what particular type you're
unpacking from, as I had understood it before. Now you seem to be
saying that it would always be a copy, but sharing state with
copy-on-write possible, which is a different situation.

> Well perhaps, but not always knowing the type of your objects at write-
> time is inherent to weakly typed languages; this happens all the time.
> Not knowing the type of the sequence to be unpacked is in a sense an
> asset; I can use this construct in a function, and unpack any sequence
> type in a manner appropriate for it. About the result of the unpacking
> I will know just as much as about the input to it; that they are the
> same type.

Just because the issue is inherent doesn't mean we should contribute
to it. Knowing that an object is an arbitrary sequence is fine if all
you want to do is iterate and index it. If you want to do anything
else, then it's important to know the type. The copy-on-write
suggestion does make the type-matching approach a bit more attractive.
On the other hand, it's also more fragile (what if the type being
unpacked can't be constructed from an iterable? For example, a
database cursor), so that approach potentially needs additional
error-handling.

Anyway, the more I think about it, that concern is really more of an
issue for straight copying. One of my pet peeves is that I prefer
list(x) for copying sequences rather than the more common x[::]. The
latter is fine if all I need is an immutable sequence of uncertain
type to iterate and index over -- but then why did I need to make a
copy? Unpacking implies different use cases, though, and maybe a good
argument can be made for it to match type.

Cameron Simpson

unread,
Dec 13, 2011, 2:11:27 PM12/13/11
to Eelco, pytho...@python.org
On 13Dec2011 00:30, Eelco <hoogendo...@gmail.com> wrote:
| On Dec 13, 1:27 am, alex23 <wuwe...@gmail.com> wrote:
| > On Dec 13, 3:12 am, Eelco <hoogendoorn.ee...@gmail.com> wrote:
| > > But to relate it to the topic of this thread: no, the syntax does not
| > > allow one to select the type of the resulting sequence. It always
| > > constructs a list.
| >
| > So by this argument, _every_ function that returns a list should take
| > an optional argument to specify an alternative form of sequence.
| >
| > What, exactly, is so onerous about coercing your list to _whatever_
| > type you want? You know, like everybody else has been.
| >
| > What does this _gain_ you other than one less line of code?
|
| 1) Turning two lines of code into a single more readable one is
| nothing to scoff at
| 2) After-the-fact conversion is O(n), getting the view you want right
| away is O(1)

Regarding (2), it has already cost you O(n) to get there. So your O(1)
is a little ingenuous.
--
Cameron Simpson <c...@zip.com.au> DoD#743
http://www.cskk.ezoshosting.com/cs/

I'm a volunteer EMT-I on our local ambulance service; one of our Paramedics
calls squid style motorcycle accidents "ZoomSplats", as in "we had a good
ZoomSplat the other night". ;-)
- Scott <tra...@rapnet.sanders.lockheed.com>

Eelco

unread,
Dec 13, 2011, 3:14:15 PM12/13/11
to
On Dec 13, 8:11 pm, Cameron Simpson <c...@zip.com.au> wrote:
> On 13Dec2011 00:30, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> | On Dec 13, 1:27 am, alex23 <wuwe...@gmail.com> wrote:
> | > On Dec 13, 3:12 am, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> | > > But to relate it to the topic of this thread: no, the syntax does not
> | > > allow one to select the type of the resulting sequence. It always
> | > > constructs a list.
> | >
> | > So by this argument, _every_ function that returns a list should take
> | > an optional argument to specify an alternative form of sequence.
> | >
> | > What, exactly, is so onerous about coercing your list to _whatever_
> | > type you want? You know, like everybody else has been.
> | >
> | > What does this _gain_ you other than one less line of code?
> |
> | 1) Turning two lines of code into a single more readable one is
> | nothing to scoff at
> | 2) After-the-fact conversion is O(n), getting the view you want right
> | away is O(1)
>
> Regarding (2), it has already cost you O(n) to get there. So your O(1)
> is a little ingenuous.

Well, yes, but if one takes a given sequence as input (at least O(n)
complexity to obtain it in the first place, indeed), and then wants
to, say, recursively unwind it, the cost of the total operation is
O(n) versus O(n^2)

And besides, O(n) < 2*O(n); perhaps of lesser concern than different
orders, but still.

Eelco

unread,
Dec 13, 2011, 3:32:43 PM12/13/11
to
On Dec 13, 7:15 pm, Ian Kelly <ian.g.ke...@gmail.com> wrote:
> On Tue, Dec 13, 2011 at 1:44 AM, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> > 'for i in llist' is not quite going to fly is it? Thats probably the
> > reason noone ever uses that construct; its not a proper sequence type.
>
> Not really a problem, because fortunately Python makes it super-easy
> to create custom iterators.
>
> def listiter(llist):
>     while llist:
>         head, llist = llist
>         yield head
>
> And you're done.  If you like, you could also wrap this up in a class
> with all the sequence-related magic methods you want, although you
> lose the simplicity of the literal syntax by doing so.  If this is
> rarely used, it is more likely because custom containers are going to
> be less efficient than built-ins, but if you want to do functional
> programming and are not overly concerned with the speed of iteration,
> this is not a bad way to do it.

Fair enough. Still, I dont readily see myself using the construct, and
I do feel myself longing for something like that to be included as a
builtin collection type. Im not sure why python is so stingy with the
collections it provides.

> > Good point. Copy-on-write semantics could be used, but really one
> > should have several linked list types reflecting the underlying
> > implementations. I would like to have an immutable singly linked list
> > builtin of the standard functional type, which you can only unpack
> > from one end and renders these issues moot, plus a builtin doubly
> > linked list with copy-on-write or copy-on-unpacking semantics.
>
> Copy-on-write could be implemented with any type.  You don't need a
> doubly linked list for that.

True

> > We are not talking black magic here; we are talking about an EXPLICIT
> > type constraint provided on the very same line.
>
> An explicit type constraint with very different semantics depending on
> what particular type you specify and what particular type you're
> unpacking from, as I had understood it before.  Now you seem to be
> saying that it would always be a copy, but sharing state with
> copy-on-write possible, which is a different situation.

Yes, I think consistent semantics over all sequence types is very
important. Although, returning a view for immutable collections like
tuples and 'functional lists' (for lack of a better term), and always
returning a copy for mutable container types (lists and doubly-linked-
lists / deques) is not so bad either, imo. Just one extra simple rule
that is clear and obvious enough.

> > Well perhaps, but not always knowing the type of your objects at write-
> > time is inherent to weakly typed languages; this happens all the time.
> > Not knowing the type of the sequence to be unpacked is in a sense an
> > asset; I can use this construct in a function, and unpack any sequence
> > type in a manner appropriate for it. About the result of the unpacking
> > I will know just as much as about the input to it; that they are the
> > same type.
>
> Just because the issue is inherent doesn't mean we should contribute
> to it.

How are we contributing to it? If we dont know the type of the RHS, we
dont know the type of the LHS either, but its not like we lost any
information. If we do know the type of the RHS, then we do know the
type of the LHS as well; its conserved.

> Anyway, the more I think about it, that concern is really more of an
> issue for straight copying.  One of my pet peeves is that I prefer
> list(x) for copying sequences rather than the more common x[::].  The
> latter is fine if all I need is an immutable sequence of uncertain
> type to iterate and index over -- but then why did I need to make a
> copy?  Unpacking implies different use cases, though, and maybe a good
> argument can be made for it to match type.

Thanks for the constructive feedback; something to think about.

Steven D'Aprano

unread,
Dec 13, 2011, 10:18:42 PM12/13/11
to
On Mon, 12 Dec 2011 09:29:11 -0800, Eelco wrote:

[quoting Jussi Piitulainen <jpii...@ling.helsinki.fi>]
>> They recognize modular arithmetic but for some reason insist that there
>> is no such _binary operation_. But as I said, I don't understand their
>> concern. (Except the related concern about some programming languages,
>> not Python, where the remainder does not behave well with respect to
>> division.)

I've never come across this, and frankly I find it implausible that
*actual* mathematicians would say that. Likely you are misunderstanding a
technical argument about remainder being a relation rather than a
bijunction. The argument would go something like this:

"Remainder is not uniquely defined. For example, the division of -42 by
-5 can be written as either:

9*-5 + 3 = -42
8*-5 + -2 = -42

so the remainder is either 3 or -2. Hence remainder is not a bijection
(1:1 function)."

The existence of two potential answers for the remainder is certainly
correct, but the conclusion that remainder is not a binary operation
doesn't follow. It is a binary relation. Mathematicians are well able to
deal with little inconveniences like this, e.g. consider the square root:

10**2 = 100
(-10)**2 = 100
therefore the square root of 100 is ±10

Mathematicians get around this by defining the square root operator √ as
*only* the principle value of the square root relation, that is, the
positive root. Hence:

√100 = 10 only

If you want both roots, you have to explicitly ask for them both: ±√100

Similarly, we can sensibly define the remainder or modulus operator to
consistently return a non-negative remainder, or to do what Python does,
which is to return a remainder with the same sign as the divisor:

>>> 42 % 5
2
>>> -42 % 5
3

>>> 42 % -5
-3
>>> -42 % -5
-2

There may be practical or logical reasons for preferring one over the
other, but either choice would make remainder a bijection. One might even
define two separate functions/operators, one for each behaviour.


> They might not be willing to define it, but as soon as we programmers
> do, well, we did.
>
> Having studied the contemporary philosophy of mathematics, their concern
> is probably that in their minds, mathematics is whatever some dead guy
> said it was, and they dont know of any dead guy ever talking about a
> modulus operation, so therefore it 'does not exist'.

You've studied the contemporary philosophy of mathematics huh?

How about studying some actual mathematics before making such absurd
pronouncements on the psychology of mathematicians?




--
Steven

Eelco

unread,
Dec 14, 2011, 2:49:43 AM12/14/11
to
On Dec 14, 4:18 am, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> > They might not be willing to define it, but as soon as we programmers
> > do, well, we did.
>
> > Having studied the contemporary philosophy of mathematics, their concern
> > is probably that in their minds, mathematics is whatever some dead guy
> > said it was, and they dont know of any dead guy ever talking about a
> > modulus operation, so therefore it 'does not exist'.
>
> You've studied the contemporary philosophy of mathematics huh?
>
> How about studying some actual mathematics before making such absurd
> pronouncements on the psychology of mathematicians?

The philosophy was just a sidehobby to the study of actual
mathematics; and you are right, studying their works is the best way
to get to know them. Speaking from that vantage point, I can say with
certainty that the vast majority of mathematicians do not have a
coherent philosophy, and they adhere to some loosely defined form of
platonism. Indeed that is absurd in a way. Even though you may trust
these people to be perfectly functioning deduction machines, you
really shouldnt expect them to give sensible answers to the question
of which are sensible axioms to adopt. They dont have a reasoned
answer to this, they will by and large defer to authority.

Jussi Piitulainen

unread,
Dec 14, 2011, 3:56:02 AM12/14/11
to
Steven D'Aprano writes:
> On Mon, 12 Dec 2011 09:29:11 -0800, Eelco wrote:
>
> [quoting Jussi Piitulainen <jpii...@ling.helsinki.fi>]
> >> They recognize modular arithmetic but for some reason insist that
> >> there is no such _binary operation_. But as I said, I don't
> >> understand their concern. (Except the related concern about some
> >> programming languages, not Python, where the remainder does not
> >> behave well with respect to division.)
>
> I've never come across this, and frankly I find it implausible that
> *actual* mathematicians would say that. Likely you are
> misunderstanding a technical argument about remainder being a
> relation rather than a bijunction. The argument would go something
> like this:

(For 'bijunction', read 'function'.)

I'm not misunderstanding any argument. There was no argument. There
was a blanket pronouncement that _in mathematics_ mod is not a binary
operator. I should learn to challenge such pronouncements and ask what
the problem is. Maybe next time.

But you are right that I don't know how actual mathematicians these
people are. I'm not a mathematician. I don't know where to draw the
line.

A Finnish actual mathematician stated a similar prejudice towards mod
as a binary operator in a Finnish group. I asked him what is wrong
with Knuth's definition (remainder after flooring division), and I
think he conceded that it's not wrong. Number theorists just choose to
work with congruence relations. I have no problem with that.

He had experience with students who confused congruences modulo some
modulus with a binary operation, and mixed up their notations because
of that. That is a reason to be suspicious, but it is a confusion on
the part of the students. Graham, Knuth, Patashnik contrast the two
concepts explicitly, no confusion there.

And I know that there are many ways to define division and remainder
so that x div y + x rem y = x. Boute's paper cited in [1] advocates a
different one and discusses others.

[1] <http://en.wikipedia.org/wiki/Modulo_operation>

But I think the argument "there are several such functions, therefore,
_in mathematics_, there is no such function" is its own caricature.

> "Remainder is not uniquely defined. For example, the division of -42
> by -5 can be written as either:
>
> 9*-5 + 3 = -42
> 8*-5 + -2 = -42
>
> so the remainder is either 3 or -2. Hence remainder is not a bijection
> (1:1 function)."

Is someone saying that _division_ is not defined because -42 div -5 is
somehow both 9 and 8? Hm, yes, I see that someone might. The two
operations, div and rem, need to be defined together.

(There is no way to make remainder a bijection. You mean it is not a
function if it is looked at in a particular way.)

[The square root was relevant but I snipped it.]

> Similarly, we can sensibly define the remainder or modulus operator
> to consistently return a non-negative remainder, or to do what
> Python does, which is to return a remainder with the same sign as
> the divisor:
...
> There may be practical or logical reasons for preferring one over
> the other, but either choice would make remainder a bijection. One
> might even define two separate functions/operators, one for each
> behaviour.

Scheme is adopting flooring division, ceiling-ing division, rounding
division, truncating division, centering division, and the Euclidean
division advocated by Boute, and the corresponding remainders. There
is no better way to bring home to a programmer the points that there
are different ways to define these, and they come as div _and_ rem.

Jussi Piitulainen

unread,
Dec 14, 2011, 4:04:04 AM12/14/11
to
Nick Dokos writes:
> Jussi Piitulainen wrote:
> > They recognize modular arithmetic but for some reason insist that
> > there is no such _binary operation_. But as I said, I don't
> > understand their concern. (Except the related concern about some
> > programming languages, not Python, where the remainder does not
> > behave well with respect to division.)
>
> They are probably arguing that it's uniquely defined only on ZxN and
> that there are different conventions to extend it to ZxZ (the
> programming languages problem that you allude to above - although I
> don't know what you mean by "does not behave well wrt division").

I think Boute [1] says Standard Pascal or some such language failed to
have x div y + x rem y = x, but I can't check the reference now. That
at least waes what I had in mind. Having x rem y but leaving it
underspecified is another such problem: then it is unspecified whether
the equation holds.

[1] <http://en.wikipedia.org/wiki/Modulo_operation>

> If you choose one convention and stick to it, it becomes a
> well-defined binary operation.

That's what I'd like to think.

Paul Rudin

unread,
Dec 14, 2011, 4:43:36 AM12/14/11
to
Steven D'Aprano <steve+comp....@pearwood.info> writes:

> The existence of two potential answers for the remainder is certainly
> correct, but the conclusion that remainder is not a binary operation
> doesn't follow. It is a binary relation.

This depends on your definition of "operation". Normally an operation is
a function, rather than just a relation.

Eelco

unread,
Dec 14, 2011, 5:09:32 AM12/14/11
to
On 14 dec, 09:56, Jussi Piitulainen <jpiit...@ling.helsinki.fi> wrote:
> Steven D'Aprano writes:
> > On Mon, 12 Dec 2011 09:29:11 -0800, Eelco wrote:
>
> > [quoting Jussi Piitulainen <jpiit...@ling.helsinki.fi>]
Indeed. Obtaining a well defined function is just a matter of picking
a convention and sticking with it.

Arguably, the most elegant thing to do is to define integer division
and remainder as a single operation; which is not only the logical
thing to do mathematically, but might work really well
programmatically too.

The semantics of python dont really allow for this though. One could
have:

d, r = a // b

But it wouldnt work that well in composite expressions; selecting the
right tuple index would be messy and a more verbose form would be
preferred. However, performance-wise its also clearly the best
solution, as one often needs both output arguments and computing them
simultaniously is most efficient.

At least numpy should have something like:
d, r = np.integer_division(a, b)

And something similar in the math module for scalars.


> > "Remainder is not uniquely defined. For example, the division of -42
> > by -5 can be written as either:
>
> >     9*-5 + 3 = -42
> >     8*-5 + -2 = -42
>
> > so the remainder is either 3 or -2. Hence remainder is not a bijection
> > (1:1 function)."
>
> Is someone saying that _division_ is not defined because -42 div -5 is
> somehow both 9 and 8? Hm, yes, I see that someone might. The two
> operations, div and rem, need to be defined together.
>
> (There is no way to make remainder a bijection. You mean it is not a
> function if it is looked at in a particular way.)

Surjection is the word you are looking for

That is, if one buys the philosophy of modernists like bourbaki in
believing there is much to be gained by such pedantry.

rusi

unread,
Dec 14, 2011, 6:47:28 AM12/14/11
to
On Dec 14, 1:56 pm, Jussi Piitulainen <jpiit...@ling.helsinki.fi>
wrote:
>
> Is someone saying that _division_ is not defined because -42 div -5 is
> somehow both 9 and 8? Hm, yes, I see that someone might. The two
> operations, div and rem, need to be defined together.
-----------------------------
Haskell defines a quot-rem pair and a div-mod pair as follows:
(from http://www.haskell.org/onlinereport/basic.html)

(x `quot` y)*y + (x `rem` y) == x
(x `div` y)*y + (x `mod` y) == x

`quot` is integer division truncated toward zero, while the result of
`div` is truncated toward negative infinity.

Chris Angelico

unread,
Dec 14, 2011, 6:53:38 AM12/14/11
to pytho...@python.org
On Wed, Dec 14, 2011 at 10:47 PM, rusi <rusto...@gmail.com> wrote:
> `quot` is integer division truncated toward zero, while the result of
> `div` is truncated toward negative infinity.

All these problems just because of negative numbers. They ought never
to have been invented.

At least nobody rounds toward positive infinity... oh wait, that's legal too.

ChrisA

Arnaud Delobelle

unread,
Dec 14, 2011, 6:55:34 AM12/14/11
to Eelco, pytho...@python.org
Please come down from your vantage point for a few moments and
consider how insulting your remarks are to people who have devoted
most of their intellectual energy to the study of mathematics. So
you've studied a bit of mathematics and a bit of philosophy? Good
start, keep working at it.

You think that every mathematician should be preoccupied with what
axioms to adopt, and why? Mathematics is a very large field of study
and yes, some mathematicians are concerned with these issues (they are
called logicians) but for most it isn't really about axioms.
Mathematics is bigger than the axioms that we use to formalise it.
Most mathematicians do not need to care about what precise
axiomatisation underlies the mathematics that they practise because
they are thinking on a much higher level. Just like we do not worry
about what machine language instruction actually performs each step of
the Python program we are writing.

You say that mathematicians defer to authority, but do you really
think that thousands of years of evolution and refinement in
mathematics are to be discarded lightly? I think not. It's good to
have original ideas, to pursue them and to believe in them, but it
would be foolish to think that they are superior to knowledge which
has been accumulated over so many generations.

You claim that mathematicians have a poor understanding of philosophy.
It may be so for many of them, but how is this a problem? I doesn't
prevent them from having a deep understanding of their field of
mathematics. Do philosophers have a good understanding of
mathematics?

Cheers,

--
Arnaud

Jussi Piitulainen

unread,
Dec 14, 2011, 7:22:27 AM12/14/11
to
Eelco writes:
> On 14 dec, 09:56, Jussi Piitulainen wrote:
> > But I think the argument "there are several such functions,
> > therefore, _in mathematics_, there is no such function" is its own
> > caricature.
>
> Indeed. Obtaining a well defined function is just a matter of
> picking a convention and sticking with it.
>
> Arguably, the most elegant thing to do is to define integer division
> and remainder as a single operation; which is not only the logical
> thing to do mathematically, but might work really well
> programmatically too.
>
> The semantics of python dont really allow for this though. One could
> have:
>
> d, r = a // b
>
> But it wouldnt work that well in composite expressions; selecting the
> right tuple index would be messy and a more verbose form would be
> preferred. However, performance-wise its also clearly the best
> solution, as one often needs both output arguments and computing them
> simultaniously is most efficient.

The current Scheme draft does this. For each rounding method, it
provides an operation that provides both the quotient and the
remainder, an operation that provides the quotient, and an operation
that provides the remainder. The both-values operation is more awkward
to compose, as you rightly say.

It's just a matter of naming them all. Python has a good default
integer division as the pair of operators // and %. Python also
supports the returning of several values from functions as tuples. It
can be done.

> > Is someone saying that _division_ is not defined because -42 div
> > -5 is somehow both 9 and 8? Hm, yes, I see that someone might. The
> > two operations, div and rem, need to be defined together.
> >
> > (There is no way to make remainder a bijection. You mean it is not
> > a function if it is looked at in a particular way.)
>
> Surjection is the word you are looking for

Um, no, I mean function. The allegedly alleged problem is that there
may be two (or more) different values for f(x,y), which makes f not a
_function_ (and the notation f(x,y) maybe inappropriate).

Surjectivity is as much beside the point as bijectivity, but I think
we have surjectivity for rem: Z * Z -> Z if we use a definition that
produces both positive and negative remainders, or rem: Z * Z -> N if
we have non-negative remainders (and include 0 in N, which is another
bone of contention). We may or may not want to exclude 0 as the
modulus, or divisor if you like. It is at least a special case.

It's injectivity that fails: 9 % 4 == 6 % 5 == 3 % 2, while Python
quite sensibly has (9, 4) != (6, 5) != (3, 2). (How I love the
chaining of the comparisons.)

> That is, if one buys the philosophy of modernists like bourbaki in
> believing there is much to be gained by such pedantry.

I think something is gained. Not sure I would call it philosophy.

Steven D'Aprano

unread,
Dec 14, 2011, 7:32:34 AM12/14/11
to
On Wed, 14 Dec 2011 10:56:02 +0200, Jussi Piitulainen wrote:

> Steven D'Aprano writes:
>> On Mon, 12 Dec 2011 09:29:11 -0800, Eelco wrote:
>>
>> [quoting Jussi Piitulainen <jpii...@ling.helsinki.fi>]
>> >> They recognize modular arithmetic but for some reason insist that
>> >> there is no such _binary operation_. But as I said, I don't
>> >> understand their concern. (Except the related concern about some
>> >> programming languages, not Python, where the remainder does not
>> >> behave well with respect to division.)
>>
>> I've never come across this, and frankly I find it implausible that
>> *actual* mathematicians would say that. Likely you are misunderstanding
>> a technical argument about remainder being a relation rather than a
>> bijunction. The argument would go something like this:
>
> (For 'bijunction', read 'function'.)

Oops, you're right of course. It's been about 20 years since I've needed
to care about the precise difference between a bijection and a function,
and I made a mistake. And then to add to my shame, I also misspelt
bijection.


> I'm not misunderstanding any argument. There was no argument. There was
> a blanket pronouncement that _in mathematics_ mod is not a binary
> operator. I should learn to challenge such pronouncements and ask what
> the problem is. Maybe next time.

So this was *one* person making that claim?

I understand that, in general, mathematicians don't have much need for a
remainder function in the same way programmers do -- modulo arithmetic is
far more important. But there's a world of difference between saying "In
mathematics, extracting the remainder is not important enough to be given
a special symbol and treated as an operator" and saying "remainder is not
a binary operator". The first is reasonable; the second is not.


> But you are right that I don't know how actual mathematicians these
> people are. I'm not a mathematician. I don't know where to draw the
> line.
>
> A Finnish actual mathematician stated a similar prejudice towards mod as
> a binary operator in a Finnish group. I asked him what is wrong with
> Knuth's definition (remainder after flooring division), and I think he
> conceded that it's not wrong. Number theorists just choose to work with
> congruence relations. I have no problem with that.

Agreed.

[...]
> (There is no way to make remainder a bijection. You mean it is not a
> function if it is looked at in a particular way.)

You're right, of course -- remainder cannot be 1:1. I don't know what I
was thinking.


--
Steven

Eelco

unread,
Dec 14, 2011, 7:33:06 AM12/14/11
to
On 14 dec, 12:55, Arnaud Delobelle <arno...@gmail.com> wrote:
Thanks, I intend to.

> You think that every mathematician should be preoccupied with what
> axioms to adopt, and why?

Of course I dont. If you wish to restrict your attention to the
exploration of the consequences of axioms others throw at you, that is
a perfectly fine specialization. Most mathematicians do exactly that,
and thats fine. But that puts them in about as ill a position to
judged what is, or shouldnt be defined, as the average plumber.
Compounding the problem is not just that they do not wish to concern
themselves with the inductive aspect of mathematics, they would like
to pretend it does not exist at all. For instance, if you point out to
them a 19th century mathematician used very different axioms than a
20th century one, (and point out they were both fine mathematicians
that attained results universally celebrated), they will typically
respond emotionally; get angry or at least annoyed. According to their
pseudo-Platonist philosophy, mathematics should not have an inductive
side, axioms are set in stone and not a human affair, and the way they
answer the question as to where knowledge about the 'correct'
mathematical axioms comes from is by an implicit or explicit appeal to
authority. They dont explain how it is that they can see 'beyond the
platonic cave' to find the 'real underlying truth', they quietly
assume somebody else has figured it out in the past, and leave it at
that.

> You say that mathematicians defer to authority, but do you really
> think that thousands of years of evolution and refinement in
> mathematics are to be discarded lightly?  I think not.  It's good to
> have original ideas, to pursue them and to believe in them, but it
> would be foolish to think that they are superior to knowledge which
> has been accumulated over so many generations.

For what its worth; insofar as my views can be pidgeonholed, im with
the classicists (pre-20th century), which indeed has a long history.
Modernists in turn discard large swaths of that. Note that its largely
an academic debate though; everybody agrees that 1+1=2. But there are
some practical consequences; if I were the designated science-Tsar,
all transfinite-analysist would be out on the street together with the
homeopaths, for instance.

> You claim that mathematicians have a poor understanding of philosophy.
>  It may be so for many of them, but how is this a problem?  I doesn't
> prevent them from having a deep understanding of their field of
> mathematics.  Do philosophers have a good understanding of
> mathematics?

As a rule of thumb: absolutely not, no. I dont think I can think of
any philosopher who turned his attention to mathematics that ever
wrote anything interesting. All the interesting writers had their
boots on mathematical ground; Quine, Brouwer, Weyl and the earlier
renaissance men like Gauss and contemporaries.

The fragmentation of disciplines is infact a major problem in my
opinion though. Most physicists take their mathematics from the ivory-
math tower, and the mathematicians shudder at the idea of listning
back to see which of what they cooked up is actually anything but
mental masturbation, in the meanwhile cranking out more gibberish
about alephs. If any well-reasoned philosophy enters into the mix, its
usually in the spare time of one of the physicists, but it is
assuredly not coming out of the philosophy department. There is
something quite wrong with that state of affairs.

Steven D'Aprano

unread,
Dec 14, 2011, 7:38:31 AM12/14/11
to
On Wed, 14 Dec 2011 02:09:32 -0800, Eelco wrote:

> Arguably, the most elegant thing to do is to define integer division and
> remainder as a single operation; which is not only the logical thing to
> do mathematically, but might work really well programmatically too.
>
> The semantics of python dont really allow for this though. One could
> have:
>
> d, r = a // b

That would be:

>>> divmod(17, 5)
(3, 2)



> But it wouldnt work that well in composite expressions; selecting the
> right tuple index would be messy and a more verbose form would be
> preferred. However, performance-wise its also clearly the best solution,
> as one often needs both output arguments and computing them
> simultaniously is most efficient.

Premature optimization.



--
Steven

Eelco

unread,
Dec 14, 2011, 7:41:23 AM12/14/11
to
On 14 dec, 13:22, Jussi Piitulainen <jpiit...@ling.helsinki.fi> wrote:
> > > Is someone saying that _division_ is not defined because -42 div
> > > -5 is somehow both 9 and 8? Hm, yes, I see that someone might. The
> > > two operations, div and rem, need to be defined together.
>
> > > (There is no way to make remainder a bijection. You mean it is not
> > > a function if it is looked at in a particular way.)
>
> > Surjection is the word you are looking for
>
> Um, no, I mean function. The allegedly alleged problem is that there
> may be two (or more) different values for f(x,y), which makes f not a
> _function_ (and the notation f(x,y) maybe inappropriate).
>
> Surjectivity is as much beside the point as bijectivity, but I think
> we have surjectivity for rem: Z * Z -> Z if we use a definition that
> produces both positive and negative remainders, or rem: Z * Z -> N if
> we have non-negative remainders (and include 0 in N, which is another
> bone of contention). We may or may not want to exclude 0 as the
> modulus, or divisor if you like. It is at least a special case.
>
> It's injectivity that fails: 9 % 4 == 6 % 5 == 3 % 2, while Python
> quite sensibly has (9, 4) != (6, 5) != (3, 2). (How I love the
> chaining of the comparisons.)

My reply was more to the statement you quoted than to yours; sorry for
the confusion. Yes, we have surjectivity and not injectivity, thats
all I was trying to say.


> > That is, if one buys the philosophy of modernists like bourbaki in
> > believing there is much to be gained by such pedantry.
>
> I think something is gained. Not sure I would call it philosophy.

Agreed; its more the notion that one stands to gain much real
knowledge by writing volumnius books about these matters that irks me,
but I guess thats more a matter of taste than philosophy.

Jussi Piitulainen

unread,
Dec 14, 2011, 8:09:48 AM12/14/11
to
Exactly what I mean. (I gave an incorrect equation but meant this.)

Jussi Piitulainen

unread,
Dec 14, 2011, 8:21:38 AM12/14/11
to
Steven D'Aprano writes:
> On Wed, 14 Dec 2011 10:56:02 +0200, Jussi Piitulainen wrote:
> > I'm not misunderstanding any argument. There was no
> > argument. There was a blanket pronouncement that _in mathematics_
> > mod is not a binary operator. I should learn to challenge such
> > pronouncements and ask what the problem is. Maybe next time.
>
> So this was *one* person making that claim?

I've seen it a few times from a few different posters, all on Usenet
or whatever this thing is nowadays called. I think I was careful to
say _some_ mathematicians, but not careful to check that any of them
were actually mathematicians speaking as mathematicians.

The context seems to be a cultural divide between maths and cs. Too
much common ground yet very different interests?

> I understand that, in general, mathematicians don't have much need
> for a remainder function in the same way programmers do -- modulo
> arithmetic is far more important. But there's a world of difference
> between saying "In mathematics, extracting the remainder is not
> important enough to be given a special symbol and treated as an
> operator" and saying "remainder is not a binary operator". The first
> is reasonable; the second is not.

Yes.

Eelco

unread,
Dec 14, 2011, 8:29:35 AM12/14/11
to
On Dec 14, 1:38 pm, Steven D'Aprano <steve
+comp.lang.pyt...@pearwood.info> wrote:
> On Wed, 14 Dec 2011 02:09:32 -0800, Eelco wrote:
> > Arguably, the most elegant thing to do is to define integer division and
> > remainder as a single operation; which is not only the logical thing to
> > do mathematically, but might work really well programmatically too.
>
> > The semantics of python dont really allow for this though. One could
> > have:
>
> > d, r = a // b
>
> That would be:
>
> >>> divmod(17, 5)
>
> (3, 2)

Cool; if only it were in the math module id be totally happy.


> > But it wouldnt work that well in composite expressions; selecting the
> > right tuple index would be messy and a more verbose form would be
> > preferred. However, performance-wise its also clearly the best solution,
> > as one often needs both output arguments and computing them
> > simultaniously is most efficient.
>
> Premature optimization.

We are talking language design here, not language use. Whether or not
this is premature is a decision that should be left to the user, if at
all possible, which in this case it very well is; just provide
multiple functions to cover all use cases (only return divisor, only
return remainder, or both)

Chris Angelico

unread,
Dec 14, 2011, 8:39:07 AM12/14/11
to pytho...@python.org
On Thu, Dec 15, 2011 at 12:29 AM, Eelco <hoogendo...@gmail.com> wrote:
> On Dec 14, 1:38 pm, Steven D'Aprano <steve
> +comp.lang.pyt...@pearwood.info> wrote:
>> That would be:
>>
>> >>> divmod(17, 5)
>>
>> (3, 2)
>
> Cool; if only it were in the math module id be totally happy.

That's easily solved.

import math
math.divmod=divmod
del __builtins__.divmod

:)

ChrisA

Ian Kelly

unread,
Dec 14, 2011, 10:45:04 AM12/14/11
to Eelco, pytho...@python.org
On Wed, Dec 14, 2011 at 6:29 AM, Eelco <hoogendo...@gmail.com> wrote:
> On Dec 14, 1:38 pm, Steven D'Aprano <steve
> +comp.lang.pyt...@pearwood.info> wrote:
>> On Wed, 14 Dec 2011 02:09:32 -0800, Eelco wrote:
>> > Arguably, the most elegant thing to do is to define integer division and
>> > remainder as a single operation; which is not only the logical thing to
>> > do mathematically, but might work really well programmatically too.
>>
>> > The semantics of python dont really allow for this though. One could
>> > have:
>>
>> > d, r = a // b
>>
>> That would be:
>>
>> >>> divmod(17, 5)
>>
>> (3, 2)
>
> Cool; if only it were in the math module id be totally happy.

Probably it's not in math because it's not a thin wrapper around a C
math library function, which is how the module was conceived. There
are already some exceptions in the math module, but I think they are
all newer than divmod.

Arnaud Delobelle

unread,
Dec 14, 2011, 11:13:59 AM12/14/11
to Eelco, pytho...@python.org
You are completely mistaken. Whatever the axiomatisation of the
mathematics that we do, we can still do the same mathematics. We
don't even need an axiomatic basis to do mathematics. In fact, the
formalisation of mathematics has always come after the mathematics
were well established. Euclid, Dedekind, Peano, Zermelo, Frankael,
didn't create axiomatic systems out of nothing. They axiomatised
pre-existing theories.

Axiomatising a theory is just one way of exploring it.

> Compounding the problem is not just that they do not wish to concern
> themselves with the inductive aspect of mathematics, they would like
> to pretend it does not exist at all. For instance, if you point out to
> them a 19th century mathematician used very different axioms than a
> 20th century one, (and point out they were both fine mathematicians
> that attained results universally celebrated), they will typically
> respond emotionally; get angry or at least annoyed. According to their
> pseudo-Platonist philosophy, mathematics should not have an inductive
> side, axioms are set in stone and not a human affair, and the way they
> answer the question as to where knowledge about the 'correct'
> mathematical axioms comes from is by an implicit or explicit appeal to
> authority. They dont explain how it is that they can see 'beyond the
> platonic cave' to find the 'real underlying truth', they quietly
> assume somebody else has figured it out in the past, and leave it at
> that.

Again, you are completely mis-representing the situation. In my
experience, most mathematicians (I'm not talking about undergraduate
students here) do not see the axioms are the root of the mathematics
that they do. Formal systems are just one way to explore mathematics.
Of course they can in some cases be very useful and enlightening.

As for inductive reasoning, I really can't understand your point. Of
course mathematicians use inductive reasoning all the time. Where do
you think the Riemann Hypothesis comes from? Or Fermat's last theorem?
Do you think that mathematicians prove results before they even think
about them? On the other hand, a result needs to be proved to be
accepted by the mathematical community, and inductive reasoning is not
valid in proofs. That's in the nature of mathematics.

>> You say that mathematicians defer to authority, but do you really
>> think that thousands of years of evolution and refinement in
>> mathematics are to be discarded lightly?  I think not.  It's good to
>> have original ideas, to pursue them and to believe in them, but it
>> would be foolish to think that they are superior to knowledge which
>> has been accumulated over so many generations.
>
> For what its worth; insofar as my views can be pidgeonholed, im with
> the classicists (pre-20th century), which indeed has a long history.
> Modernists in turn discard large swaths of that. Note that its largely
> an academic debate though; everybody agrees that 1+1=2. But there are
> some practical consequences; if I were the designated science-Tsar,
> all transfinite-analysist would be out on the street together with the
> homeopaths, for instance.

It's telling that on the one hand you criticise mathematicians for not
questioning the "axioms which are thrown at them", on the other hand
you feel able to discard a perfectly fine piece of mathematics, that
of the study of transfinite numbers, because it doesn't fit nicely
with traditional views. The fact is that at the end of the 19th
century mathematics had reached a crisis point.

>> You claim that mathematicians have a poor understanding of philosophy.
>>  It may be so for many of them, but how is this a problem?  I doesn't
>> prevent them from having a deep understanding of their field of
>> mathematics.  Do philosophers have a good understanding of
>> mathematics?
>
> As a rule of thumb: absolutely not, no. I dont think I can think of
> any philosopher who turned his attention to mathematics that ever
> wrote anything interesting. All the interesting writers had their
> boots on mathematical ground; Quine, Brouwer, Weyl and the earlier
> renaissance men like Gauss and contemporaries.
>
> The fragmentation of disciplines is infact a major problem in my
> opinion though. Most physicists take their mathematics from the ivory-
> math tower, and the mathematicians shudder at the idea of listning
> back to see which of what they cooked up is actually anything but
> mental masturbation, in the meanwhile cranking out more gibberish
> about alephs.

Only a minority of mathematicians have an interest in "alephs", as you
call them. IMHO, the mathematics they do is perfectly valid. The
exploration of the continuum hypothesis has led to the creation of
very powerful mathematical techniques and gives an insight into the
very foundations of mathematics. Again, on the one hand you criticise
mathematicians for not questioning the axioms they work with, but
those who investigate the way these axioms interact you accuse of
"mental masturbation".

--
Arnaud

Eelco

unread,
Dec 14, 2011, 12:15:47 PM12/14/11
to
'Kindof' off-topic, but what the hell :).

On Dec 14, 5:13 pm, Arnaud Delobelle <arno...@gmail.com> wrote:
Yes, axiomization is to some extent a side-show. We know what it is
that we want mathematics to be, and we try to find the axioms that
lead to those conclusions. Not qualitatively different from any other
form of induction (of the epistemological rather than mathematical
kind). Still, different axioms or meta-mathematics give subtly
different results, not to mention are as different to work with as
assembler and haskell. There are no alephs if you start from a
constructive basis, for instance.

Im not sure what 'Axiomatising a theory is just one way of exploring
it' means. One does not axiomatize a single theory; that would be
trivial (A is true because thats what I define A to be). One
constructs a single set of axioms from which a nontrivial set of
theorems follow.

The way id put it, is that axiomazation is about being explicit in
what it is that you assume, trying to minimalize that, and being
systematic about what conclusions that forces you to embrace.

Could you be more precise as to how I am 'completely mistaken'? I
acknowledge that my views are outside the mainstream, so its no news
to me many would think so, but it would be nice to know what im
arguing against in this thread precisely.

> > Compounding the problem is not just that they do not wish to concern
> > themselves with the inductive aspect of mathematics, they would like
> > to pretend it does not exist at all. For instance, if you point out to
> > them a 19th century mathematician used very different axioms than a
> > 20th century one, (and point out they were both fine mathematicians
> > that attained results universally celebrated), they will typically
> > respond emotionally; get angry or at least annoyed. According to their
> > pseudo-Platonist philosophy, mathematics should not have an inductive
> > side, axioms are set in stone and not a human affair, and the way they
> > answer the question as to where knowledge about the 'correct'
> > mathematical axioms comes from is by an implicit or explicit appeal to
> > authority. They dont explain how it is that they can see 'beyond the
> > platonic cave' to find the 'real underlying truth', they quietly
> > assume somebody else has figured it out in the past, and leave it at
> > that.
>
> Again, you are completely mis-representing the situation.  In my
> experience, most mathematicians (I'm not talking about undergraduate
> students here) do not see the axioms are the root of the mathematics
> that they do.  Formal systems are just one way to explore mathematics.
>  Of course they can in some cases be very useful and enlightening.

Its your word versus mine I suppose.

> As for inductive reasoning, I really can't understand your point.  Of
> course mathematicians use inductive reasoning all the time.  Where do
> you think the Riemann Hypothesis comes from? Or Fermat's last theorem?
>  Do you think that mathematicians prove results before they even think
> about them?  On the other hand, a result needs to be proved to be
> accepted by the mathematical community, and inductive reasoning is not
> valid in proofs.  That's in the nature of mathematics.

We mean something different by the term it seems. What you describe, I
would call intuition. Which is indeed very important in mathematics,
and indeed no substitute for deduction. By induction, I mean the
process of reducing particular facts/observations/theorems to a more
compact body of theory/axioms that imply the same. In a way, its the
inverse of deduction (seeing which body of conclusions follows from a
given set of axioms)


> >> You say that mathematicians defer to authority, but do you really
> >> think that thousands of years of evolution and refinement in
> >> mathematics are to be discarded lightly?  I think not.  It's good to
> >> have original ideas, to pursue them and to believe in them, but it
> >> would be foolish to think that they are superior to knowledge which
> >> has been accumulated over so many generations.
>
> > For what its worth; insofar as my views can be pidgeonholed, im with
> > the classicists (pre-20th century), which indeed has a long history.
> > Modernists in turn discard large swaths of that. Note that its largely
> > an academic debate though; everybody agrees that 1+1=2. But there are
> > some practical consequences; if I were the designated science-Tsar,
> > all transfinite-analysist would be out on the street together with the
> > homeopaths, for instance.
>
> It's telling that on the one hand you criticise mathematicians for not
> questioning the "axioms which are thrown at them", on the other hand
> you feel able to discard a perfectly fine piece of mathematics, that
> of the study of transfinite numbers, because it doesn't fit nicely
> with traditional views.  The fact is that at the end of the 19th
> century mathematics had reached a crisis point.

It is a shame you both fail to specify by what metric it is a
perfectly fine piece of mathematics, and yet more egregiously, put
words into my mouth as to what I think is wrong with it. That makes
for slow and painful debating.

My objection to transfinite analysis is that it is not scientific, in
the sense that I judge most parts of mathematics to be. I am not
questioning its deductive validity; that something mathematicians can
generally be trusted with. My contention is that the axioms that give
rise to transfinite analysis are of the same 'validity' as any random
set of axioms you could pull from a random number generator. All sets
of axioms have implications, but we dont study all possible sets of
axioms. Studying a random set of axioms leads to an arbitrary number
of nonsensical results; nonsensical in the common day useage of the
word, and nonsensical in a philosophical sense; as not relating to any
sense-impressions, or synthetic propositions.

Transfinite analysis does not give any results of any relevance that
im aware of, but id love to be proven wrong. The fact that we do get
this cancerous outgrowth of implications called transfinite analysis
is a hint that these axioms are borked; not a beautiful view on a
world of truth beyond our senses that only mathematics can give us.
(again, in my minority opinion). Id love to debate you as to where
exactly I suspect things went wrong, but its a lengthy story, and its
really not the right place I suppose; nor the right time, I have to
cook.

> >> You claim that mathematicians have a poor understanding of philosophy.
> >>  It may be so for many of them, but how is this a problem?  I doesn't
> >> prevent them from having a deep understanding of their field of
> >> mathematics.  Do philosophers have a good understanding of
> >> mathematics?
>
> > As a rule of thumb: absolutely not, no. I dont think I can think of
> > any philosopher who turned his attention to mathematics that ever
> > wrote anything interesting. All the interesting writers had their
> > boots on mathematical ground; Quine, Brouwer, Weyl and the earlier
> > renaissance men like Gauss and contemporaries.
>
> > The fragmentation of disciplines is infact a major problem in my
> > opinion though. Most physicists take their mathematics from the ivory-
> > math tower, and the mathematicians shudder at the idea of listning
> > back to see which of what they cooked up is actually anything but
> > mental masturbation, in the meanwhile cranking out more gibberish
> > about alephs.
>
> Only a minority of mathematicians have an interest in "alephs", as you
> call them.

I know.

> IMHO, the mathematics they do is perfectly valid.  The
> exploration of the continuum hypothesis has led to the creation of
> very powerful mathematical techniques and gives an insight into the
> very foundations of mathematics.

Like I said, I dont question its deductive validity. As for providing
insight into the deductive process; probably, but so do Sudoku's, and
I dont see them being state-sponsored across the globe. Any self-
created puzzle will do for that purpose; and I suspect the same time
spent on real puzzles has the same effect, plus more.

> Again, on the one hand you criticise
> mathematicians for not questioning the axioms they work with, but
> those who investigate the way these axioms interact you accuse of
> "mental masturbation".

In my, admittedly outsider view of things, transfinite analysis are
rather sad deduction machines. Id be delighted if you could show me
one that has done some kind of reflection as to why they so fervently
keep chasing the ghosts that the likes of Hilbert and Russel conjured
up for them, or that have even bothered to expose themselves to the
mockery that the likes of Gauss would have showered upon them (or
Feynmann, for a more recent but imperfect analog).

Terry Reedy

unread,
Dec 14, 2011, 3:57:40 PM12/14/11
to pytho...@python.org
On 12/14/2011 5:09 AM, Eelco wrote:

> Arguably, the most elegant thing to do is to define integer division
> and remainder as a single operation;

It actually is, as quotient and remainder are calculated together. The
microprocessors I know of expose this (as does Python). 'a divmod b'
puts the quotient in one register and the remainder in another. If you
ask for just one of the two values, both are calculated and one is
grabbed while the other is returned.

> which is not only the logical
> thing to do mathematically, but might work really well
> programmatically too.
>
> The semantics of python dont really allow for this though. One could
> have:
>
> d, r = a // b

>>> a,b = divmod(10,3)
>>> a,b
(3, 1)

With CPython, int.__divmod__ lightly wraps and exposes the processor
operation.

> But it wouldnt work that well in composite expressions; selecting the
> right tuple index would be messy and a more verbose form would be
> preferred.

That is why we have
>>> a == 10 // 3
True
>>> b == 10 % 3
True

In both cases, I believe CPython calls int.__divmod__ (or the lower
level equivalent) to calculate both values, and one is returned while
the other is ignored. It it the same when one does long division by hand.

> However, performance-wise its also clearly the best
> solution, as one often needs both output arguments and computing them
> simultaniously is most efficient.

As indicated above, there is really no choice but to calculate both at
once. If one needs both a//b and a%b, one should explicitly call divmod
once and save (name) both values, instead of calling it implicitly twice
and tossing half the answer each time.

--
Terry Jan Reedy

rusi

unread,
Dec 14, 2011, 10:43:21 PM12/14/11
to
On Dec 14, 10:15 pm, Eelco <hoogendoorn.ee...@gmail.com> wrote:
> 'Kindof' off-topic, but what the hell :).

<deja-vu>
We keep having these debates -- so I wonder how off-topic it is...
And so do famous CSists:
http://research.microsoft.com/en-us/um/people/gurevich/opera/123.pdf
</deja-vu>

:
:
> > Again, you are completely mis-representing the situation.  In my
> > experience, most mathematicians (I'm not talking about undergraduate
> > students here) do not see the axioms are the root of the mathematics
> > that they do.  Formal systems are just one way to explore mathematics.
> >  Of course they can in some cases be very useful and enlightening.
>
> Its your word versus mine I suppose.

Some older discussions:

http://groups.google.com/group/comp.lang.python/browse_thread/thread/46435c36f3a13621/896579b757126243?lnk=gst&q=rusi+steven+platonism#896579b757126243

http://groups.google.com/group/comp.lang.python/browse_thread/thread/d36dcd2e2e175d1e/45dd596bc050ac2d?lnk=gst&q=rusi+steven+platonism#45dd596bc050ac2d
It is loading more messages.
0 new messages