To me, Python can't seem to make up its mind with these weird (IMHO) stand-alone methods like join(), sum(), any(), etc. That's the kind of thing I'd expect in a functional language, not in an OO language.
To me, Python can't seem to make up its mind with these weird (IMHO) stand-alone methods like join(), sum(), any(), etc. That's the kind of thing I'd expect in a functional language, not in an OO language.
"list.join(str)" and "str.join(list)" are not the same. The list.join syntax only works for lists, but str.join takes any sequence, which is anything that implements the sequence protocol, including lists, dicts, sets, trees, heaps, iterators in general, and a limitless variety of user-defined sequences. Once you understand that it's not a matter of picking two spellings of the same functionality, but actually radically different functionality between the two spellings. (Putting this on string rather than it being a free-floating function is perhaps dubious, but at least the operation does have a sort of irreducible stringy-ness to it in a way that it does not have a listy-ness to it.)
If you make the "join" a method of list, then you have grotesquely cut its functionality. Now you are requiring people to implement their own join on every other object that implements the sequence protocol, which is just silly. (Of course if they have other needs they may still implement something else, but why not give them the option of the default?)
This is generic-programming through duck-types, not OO. Similarly for all the other examples you cite and quite a few more; putting them on "list" is not "the sensible thing to do", it would be a grave error.
Python 2.5.2 (r252:60911, May 7 2008, 15:19:09)
...
>>> ", ".join(str(x) for x in xrange(10))
'0, 1, 2, 3, 4, 5, 6, 7, 8, 9'
That's not a list in there.To me it seems unnatural to see join on string because string feels more 'basic' than a sequence, so I would expect the sequence to know about strings rather than the opposite. But the argument against join on string is not as strong as the argument against sum on array.
In this case, their objection is that it violates their intuition, but the placement of join is a natural result of how Python's types are defined. Defining join on all sequences would have real (annoying) implications.
How does that not fit this situation? Because there's actually a good technical reason to do it the way it's currently done? Wouldn't that imply that there should be even less discussion of it?
Bike-shedding is when the arguments have relatively the same merit, and the choice makes little difference in the end. I don't think either applies.
An example of bike-shedding in this context would be renaming join to fuse.
Bitching that '\n'.join(alist) should be alist.join('\n') is a favorite pastime of people who want to demonstrate that they've heard of OO programming. It's a perfect example of bike-shedding.
http://www.freebsd.org/cgi/getmsg.cgi?fetch=506636+517178+/u...
http://en.wikipedia.org/wiki/Color_of_the_bikeshed
http://catb.org/jargon/html/B/bikeshedding.html
http://www.urbandictionary.com/define.php?term=bikeshedding
All of these definitions require the issue to be of marginal benefit to the overall problem. The observation that people feel the need to contribute their opinion on matters they feel they know about is why bikeshedding happens, but it's not bikeshedding itself.
The more important point was that you can't call someone wrong when they're using the actual, original definition correctly, which I was (the link I included was how the term entered the geek lexicon).
But. All of this is a digression. The entire reason I said I don't think this is bikeshedding is because I think there's value in discussing why join is a member of strings and not sequences in Python. Understanding this point helps in understanding Python's design.
BTW, there are other cases where Python has gone the other way. Multiline lambdas, for instance. The case against multiline lambdas is essentially that they look ugly - they let you embed an indentation-sensitive block in what might be a parenthesized expression, which means you need some dangling delimiters. But they're certainly useful, as Scheme/Lisp/Ocaml/etc. have shown.
It's similar to the compare-constants-from-the-left idiom in languages where assignment is a valid expression (notably C). If you always write "if (NULL == foo)" instead of "if (foo == NULL)", you eliminate a whole class of bugs. But I've seen very few programmers do this, because it "feels" wrong to a lot of people.
I shouldn't have said that the join argument was "purely aesthetic", I should have said that it was "purely aesthetic and trivial", because you're right, aesthetics are important, and (imo) are reason enough to disallow multiline lambdas. How do you cross the line from trivial to not? I don't know exactly, but I do know that swapping the position of the involved nouns and taking up the same number of characters for a good technical reason doesn't.
I would support making the C compiler just disallow "if (x = y)".
$.each(my_array, function(val) {
// Do something
// Do something else
});
That's putting the most complicated object last, and feels much more natural than: $.each(function(val) {
// Do something
// Do something else
}, my_array);
I suspect it's more that English has trained us to read "noun verb object" sentences. In an if statement, `foo` feels like the subject, and then "== NULL" is the predicate. In an array map, the array is the subject, and the function is the predicate. In the Python join example, the array feels like the subject, "join" is the verb, and "," is the object, which is what makes it seem so awkward. Ruby's array.sum feels more natural because the array is the subject and sum is an intransitive verb.See http://docs.python.org/library/stdtypes.html#iterator-types and http://docs.python.org/library/stdtypes.html#http://docs.pyt...
You can't put join there. Because then you have to put add, any, none, all, reduce, filter, and so on and so forth for at least another ten or twenty base methods (ignoring everything else that applies just to the sequence protocol written by users), which every single implementation would then have to provide. No matter how abstractly beautiful it may be that this implements object-orientation dogma, in practice it's an idea too stupid to even begin to consider.
If you write your own class that is iterable but doesn't inherit from list, it would not have a join() method unless you wrote one. But it works as an argument to '\n'.join() for free since that can operate on anything iterable.
The other problem with sticking methods like join(), sum() and any() onto collections is that whenever a new method is added (for example, any() was added in python 2.5), it would cause confusion for anyone who has a list-like class that happens to have a method with that name that does something different.
In Ruby, this is accomplished through Array#inject, which allows you to specify the procedure that gets applied as you're joining. This makes it easy to do summing, string concatenation, etc, etc.