The format of map seems off. Coming from JS, all the functions come second. I think this approach is superior. Currently: map(lambda x: chr(ord('a')+x), range(26)) # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'] But I think this reads better: map(range(26), lambda x: chr(ord('a')+x)) Currently that results in: TypeError: argument 2 to map() must support iteration Also, how are we to tell what supports map()? Any iterable should be able to map via: range(26).map(lambda x: chr(ord('a')+x))) While the line length is the same, I think the latter is much more readable, and the member method avoids parameter order confusion For the global map(), having the iterable first also increases reliability because the lambda function is highly variable in length, where as parameter names are generally shorter than even the longest lambda expression. More readable: IMHO: map(in, lambda x: chr(ord('a')+x)) out = map(out, lambda x: chr(ord('a')+x)) out = map(out, lambda x: chr(ord('a')+x)) Less readable (I have to parse the lambda): map(lambda x: chr(ord('a')+x), in) out = map(lambda x: chr(ord('a')+x), out) out = map(lambda x: chr(ord('a')+x), out) But I contend: range(26).map(lambda x: chr(ord('a')+x))) is superior to all.
We're not going to break every version of Python since 0.9 because Javascript does something a certain way. Whatever might be better abstractly, this is well established. As to adding a `.map()` method to *every* iterable... just how would you propose to do that given that it's really easy and common to write custom iterables. How do you propose to change every class ever written by users? (including ones that already define `.map()` with some other meaning than the one you suggest)? On Wed, Sep 13, 2017 at 8:09 AM, Jason H <jhihn@gmx.com> wrote:
The format of map seems off. Coming from JS, all the functions come second. I think this approach is superior.
Currently: map(lambda x: chr(ord('a')+x), range(26)) # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
But I think this reads better: map(range(26), lambda x: chr(ord('a')+x))
Currently that results in: TypeError: argument 2 to map() must support iteration
Also, how are we to tell what supports map()? Any iterable should be able to map via: range(26).map(lambda x: chr(ord('a')+x)))
While the line length is the same, I think the latter is much more readable, and the member method avoids parameter order confusion
For the global map(), having the iterable first also increases reliability because the lambda function is highly variable in length, where as parameter names are generally shorter than even the longest lambda expression.
More readable: IMHO: map(in, lambda x: chr(ord('a')+x)) out = map(out, lambda x: chr(ord('a')+x)) out = map(out, lambda x: chr(ord('a')+x))
Less readable (I have to parse the lambda): map(lambda x: chr(ord('a')+x), in) out = map(lambda x: chr(ord('a')+x), out) out = map(lambda x: chr(ord('a')+x), out)
But I contend: range(26).map(lambda x: chr(ord('a')+x))) is superior to all.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Keeping medicines from the bloodstreams of the sick; food from the bellies of the hungry; books from the hands of the uneducated; technology from the underdeveloped; and putting advocates of freedom in prisons. Intellectual property is to the 21st century what the slave trade was to the 16th.
While I agree that the method calling syntax is nicer, I disagree with flipping the argument error for three main reasons. First: it violates the signature entirely The signature to map is map(function, *iterables). Python’s map is more like Haskell’s zipWith. Making the function last would either ruin the signature or would slow down performance. Second: currying If you ever curry a function in Python using functools.partial, having the most common arguments first is crucial. (You’re more likely to apply the same function to multiple iterables than to apply several functions on the same exact iterable). Thirdly: the change would make several functional programming packages have incompatible APIs. Currently libraries like PyToolz/Cytoolz and funcy have APIs that require function-first argument order. Changing the argument order would be disruptive to most Python FP packages/frameworks. So while I agree with you that “iterable.map(fn)” is more readable, I think changing the argument order would be too much of a breaking change, and there is no practical way to add “iterable.map(fn)” to every iterable type. - Ed
On Sep 13, 2017, at 11:09, Jason H <jhihn@gmx.com> wrote:
The format of map seems off. Coming from JS, all the functions come second. I think this approach is superior.
Currently: map(lambda x: chr(ord('a')+x), range(26)) # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
But I think this reads better: map(range(26), lambda x: chr(ord('a')+x))
Currently that results in: TypeError: argument 2 to map() must support iteration
Also, how are we to tell what supports map()? Any iterable should be able to map via: range(26).map(lambda x: chr(ord('a')+x)))
While the line length is the same, I think the latter is much more readable, and the member method avoids parameter order confusion
For the global map(), having the iterable first also increases reliability because the lambda function is highly variable in length, where as parameter names are generally shorter than even the longest lambda expression.
More readable: IMHO: map(in, lambda x: chr(ord('a')+x)) out = map(out, lambda x: chr(ord('a')+x)) out = map(out, lambda x: chr(ord('a')+x))
Less readable (I have to parse the lambda): map(lambda x: chr(ord('a')+x), in) out = map(lambda x: chr(ord('a')+x), out) out = map(lambda x: chr(ord('a')+x), out)
But I contend: range(26).map(lambda x: chr(ord('a')+x))) is superior to all.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
Sent: Wednesday, September 13, 2017 at 11:23 AM From: "Edward Minnix" <egregius313@gmail.com> To: "Jason H" <jhihn@gmx.com> Cc: Python-Ideas <python-ideas@python.org> Subject: Re: [Python-ideas] Make map() better
While I agree that the method calling syntax is nicer, I disagree with flipping the argument error for three main reasons.
First: it violates the signature entirely The signature to map is map(function, *iterables). Python’s map is more like Haskell’s zipWith. Making the function last would either ruin the signature or would slow down performance.
Second: currying If you ever curry a function in Python using functools.partial, having the most common arguments first is crucial. (You’re more likely to apply the same function to multiple iterables than to apply several functions on the same exact iterable).
Thirdly: the change would make several functional programming packages have incompatible APIs. Currently libraries like PyToolz/Cytoolz and funcy have APIs that require function-first argument order. Changing the argument order would be disruptive to most Python FP packages/frameworks.
So while I agree with you that “iterable.map(fn)” is more readable, I think changing the argument order would be too much of a breaking change, and there is no practical way to add “iterable.map(fn)” to every iterable type.
Thanks for the insights. I don't think it would be that breaking: def remap_map(a1, a2): if hasattr(a1, '__call__'): return map(a1, a2) elif hasattr(a2, '__call__'): return map(a2,a1) else: raise NotCallable # Exception neither is callable I'm rather surprised that there isn't a Iterable class which dict and list derive from. If that were added to just dict and list, I think it would cover 98% of cases, and adding Iterable would be reasonable in the remaining scenarios.
On Wed, Sep 13, 2017 at 10:54 AM, Jason H <jhihn@gmx.com> wrote:
Thanks for the insights. I don't think it would be that breaking:
def remap_map(a1, a2): if hasattr(a1, '__call__'): return map(a1, a2) elif hasattr(a2, '__call__'): return map(a2,a1) else: raise NotCallable # Exception neither is callable
I think it's better to be parsimonious and adhere to the "there is one way to do it" design principle. On the matter of style, map with a lambda is more pleasing as `(expr-x for x in iterable)` rather than `map(lambda x: expr-x, iterable)`. If you need to handle multiple iterables, they can be zip'd.
I'm rather surprised that there isn't a Iterable class which dict and list derive from. If that were added to just dict and list, I think it would cover 98% of cases, and adding Iterable would be reasonable in the remaining scenarios.
For checking, there's `collections.abc.Iterable` and neighbors that can look at the interface easily, but I don't think the C-implemented, built-in types spring from them. Nick
Jason H schrieb am 13.09.2017 um 17:54:
I'm rather surprised that there isn't a Iterable class which dict and list derive from. If that were added to just dict and list, I think it would cover 98% of cases, and adding Iterable would be reasonable in the remaining scenarios.
Would you then always have to inherit from that class in order to make a type iterable? That would be fairly annoying... The iterable and iterator protocols are extremely simple, and that's a great feature. And look, map() even works with all of them, without inheritance, registration, and whatnot. It's so easy! Stefan
Sent: Wednesday, September 13, 2017 at 3:57 PM From: "Stefan Behnel" <stefan_ml@behnel.de> To: python-ideas@python.org Subject: Re: [Python-ideas] Make map() better
Jason H schrieb am 13.09.2017 um 17:54:
I'm rather surprised that there isn't a Iterable class which dict and list derive from. If that were added to just dict and list, I think it would cover 98% of cases, and adding Iterable would be reasonable in the remaining scenarios.
Would you then always have to inherit from that class in order to make a type iterable? That would be fairly annoying...
The iterable and iterator protocols are extremely simple, and that's a great feature.
And look, map() even works with all of them, without inheritance, registration, and whatnot. It's so easy!
Define easy. It's far easier for me to do a dir(dict) and see what I can do with it. This is what python does after all. "Does it have the interface I expect?" Global functions like len(), min(), max(), map(), etc(), don't really tell me the full story. len(7) makes no sense. I can attempt to call a function with an invalid argument. [].len() makes more sense. Python is weird in that there are these special magical globals that operate on many things. Why is it ','.join(iterable), why isn't there join(',', iterable) At what point does a method become a global? A member? Do we take the path that everything is a global? Or should all methods be members? So far it seems arbitrary.
On Wed, Sep 13, 2017 at 11:05:26PM +0200, Jason H wrote:
And look, map() even works with all of them, without inheritance, registration, and whatnot. It's so easy!
Define easy.
Opposite of hard or difficult. You want to map a function? map(function, values) is all it takes. You don't have to care whether the collection of values supports the map() method, or whether the class calls it "apply", or "Map", or something else. All you need care about is that the individual items inside the iterable are valid for the function, but you would need to do that regardless of how you call it. [1, 2, 3, {}, 5].map(plusone) # will fail
It's far easier for me to do a dir(dict) and see what I can do with it.
And what of the functions that dict doesn't know about?
This is what python does after all. "Does it have the interface I expect?" Global functions like len(), min(), max(), map(), etc(), don't really tell me the full story. len(7) makes no sense. I can attempt to call a function with an invalid argument.
And you can attempt to call a non-existent method: x = 7 x.len() Or should that be length() or size() or count() or what?
[].len() makes more sense.
Why? Getting the length of a sequence or iterator is not specifically a list operation, it is a generic operation that can apply to many different kinds of things.
Python is weird in that there are these special magical globals
The word you want is "function".
that operate on many things.
What makes that weird? Even Javascript has functions. So do C, Pascal, Haskell, C++, Lisp, Scheme, and thousands of other languages.
Why is it ','.join(iterable), why isn't there join(',', iterable) At what point does a method become a global? A member? Do we take the path that everything is a global? Or should all methods be members? So far it seems arbitrary.
Okay, its arbitrary. Why is it called [].len instead of [].length or {}.size? Why None instead of nil or null or nul or NULL or NOTHING? Many decisions in programming languages are arbitrary. -- Steve
On 2017-09-14 03:55, Steven D'Aprano wrote:
On Wed, Sep 13, 2017 at 11:05:26PM +0200, Jason H wrote:
And look, map() even works with all of them, without inheritance, registration, and whatnot. It's so easy!
Define easy.
Opposite of hard or difficult.
You want to map a function?
map(function, values)
is all it takes. You don't have to care whether the collection of values supports the map() method, or whether the class calls it "apply", or "Map", or something else. All you need care about is that the individual items inside the iterable are valid for the function, but you would need to do that regardless of how you call it.
[1, 2, 3, {}, 5].map(plusone) # will fail
It's far easier for me to do a dir(dict) and see what I can do with it.
And what of the functions that dict doesn't know about?
This is what python does after all. "Does it have the interface I expect?" Global functions like len(), min(), max(), map(), etc(), don't really tell me the full story. len(7) makes no sense. I can attempt to call a function with an invalid argument.
And you can attempt to call a non-existent method:
x = 7 x.len()
Or should that be length() or size() or count() or what?
[].len() makes more sense.
Why? Getting the length of a sequence or iterator is not specifically a list operation, it is a generic operation that can apply to many different kinds of things.
Python is weird in that there are these special magical globals
The word you want is "function".
that operate on many things.
What makes that weird? Even Javascript has functions. So do C, Pascal, Haskell, C++, Lisp, Scheme, and thousands of other languages.
Why is it ','.join(iterable), why isn't there join(',', iterable) At what point does a method become a global? A member? Do we take the path that everything is a global? Or should all methods be members? So far it seems arbitrary.
Okay, its arbitrary.
Why is it called [].len instead of [].length or {}.size? Why None instead of nil or null or nul or NULL or NOTHING?
Many decisions in programming languages are arbitrary.
In Java, strings have .length(), arrays have .length, and collections have .size().
Why is it ','.join(iterable), why isn't there join(',', iterable)
Because join apply on a string, and strings are defined by the str class, not by a specific protocol (unlike iterables). 2017-09-14 18:43 GMT+02:00 MRAB <python@mrabarnett.plus.com>:
On 2017-09-14 03:55, Steven D'Aprano wrote:
On Wed, Sep 13, 2017 at 11:05:26PM +0200, Jason H wrote:
And look, map() even works with all of them, without inheritance,
registration, and whatnot. It's so easy!
Define easy.
Opposite of hard or difficult.
You want to map a function?
map(function, values)
is all it takes. You don't have to care whether the collection of values supports the map() method, or whether the class calls it "apply", or "Map", or something else. All you need care about is that the individual items inside the iterable are valid for the function, but you would need to do that regardless of how you call it.
[1, 2, 3, {}, 5].map(plusone) # will fail
It's far easier for me to do a dir(dict) and see what I can do with it.
And what of the functions that dict doesn't know about?
This is what python does after all. "Does it have the interface I
expect?" Global functions like len(), min(), max(), map(), etc(), don't really tell me the full story. len(7) makes no sense. I can attempt to call a function with an invalid argument.
And you can attempt to call a non-existent method:
x = 7 x.len()
Or should that be length() or size() or count() or what?
[].len() makes more sense.
Why? Getting the length of a sequence or iterator is not specifically a list operation, it is a generic operation that can apply to many different kinds of things.
Python is weird in that there are these special magical globals
The word you want is "function".
that operate on many things.
What makes that weird? Even Javascript has functions. So do C, Pascal, Haskell, C++, Lisp, Scheme, and thousands of other languages.
Why is it ','.join(iterable), why isn't there join(',', iterable) At what
point does a method become a global? A member? Do we take the path that everything is a global? Or should all methods be members? So far it seems arbitrary.
Okay, its arbitrary.
Why is it called [].len instead of [].length or {}.size? Why None instead of nil or null or nul or NULL or NOTHING?
Many decisions in programming languages are arbitrary.
In Java, strings have .length(), arrays have .length, and collections
have .size().
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Antoine Rozo
Why is it ','.join(iterable), why isn't there join(',', iterable) Because join apply on a string, and strings are defined by the str class, not by a specific protocol (unlike iterables). Why? I can iterate over a string. [c for c in 'abc'] It certainly behaves like one... I'd say this is inconsistent because there is no __iter__() and next() on the str class.
Many decisions in programming languages are arbitrary. In Java, strings have .length(), arrays have .length, and collections have .size(). You don't have to write wholly new functions. We could keep the globals and just add wrappers: class Iterable: def length(self): return len(self) # list or dict def map(self, f): return map(f, self)) # list def map(self, f): return map(f, self.iteritems()) # dict
And this circles back around to my original point. Python is the only language people use. Only a very small few get to have 1 language. The rest of us are stuck switching languages, experiencing pain and ambiguity when writing code. I think that's a legitimate criticism. We've got Python as an outlier with no property or method for length of a collection (C++/Java/JS). I don't care if it's implemented as all the same function, but being the exception is a drawback. (FWIW, I don't like size() unless it's referring to storage size which isn't necessarily equal to the number of iterables.) I do think Python is superior in many, many, ways to all other languages, but as Python and JS skills are often desired in the same engineer, it seems that we're making it harder on the majority of the labor force. As for the English language parsing of map(f, list), it's a simple matter of taking what you have - an iterable, and applying the function to them. You start with shoes that you want to be shined, not the act of shining then apply that to the shoes. It's like the saying when all you have is a hammer, everything starts to look like a nail. You're better off only hammering nails.
On Fri, Sep 15, 2017 at 5:06 AM, Jason H <jhihn@gmx.com> wrote:
Why is it ','.join(iterable), why isn't there join(',', iterable)
Because join apply on a string, and strings are defined by the str class, not by a specific protocol (unlike iterables). Why? I can iterate over a string. [c for c in 'abc'] It certainly behaves like one... I'd say this is inconsistent because there is no __iter__() and next() on the str class.
There is __iter__, but no next() or __next__() on the string itself. __iter__ makes something iterable; __next__ is on iterators, but not on all iterables.
"abc".__iter__() <str_iterator object at 0x7fce2b672550>
I do think Python is superior in many, many, ways to all other languages, but as Python and JS skills are often desired in the same engineer, it seems that we're making it harder on the majority of the labor force.
"We" are making it harder? Who's "we"? Python predates JavaScript by a few years, and the latter language was spun up in less than two weeks in order to create an 'edge' in the browser wars. So I don't think anyone really planned for anyone to write multi-language code involving Python and JS. ChrisA
Why? I can iterate over a string. [c for c in 'abc'] It certainly behaves like one... I'd say this is inconsistent because there is no __iter__() and next() on the str class.
Yes, strings are iterables. You can use a string as argument of str.join method. But only strings can be used as separators, so there is non need for a generic join method for all types of separators. Python is well designed, you are just not used to it 2017-09-14 21:31 GMT+02:00 Chris Angelico <rosuav@gmail.com>:
On Fri, Sep 15, 2017 at 5:06 AM, Jason H <jhihn@gmx.com> wrote:
Why is it ','.join(iterable), why isn't there join(',', iterable)
Because join apply on a string, and strings are defined by the str
class, not by a specific protocol (unlike iterables).
Why? I can iterate over a string. [c for c in 'abc'] It certainly behaves like one... I'd say this is inconsistent because there is no __iter__() and next() on the str class.
There is __iter__, but no next() or __next__() on the string itself. __iter__ makes something iterable; __next__ is on iterators, but not on all iterables.
"abc".__iter__() <str_iterator object at 0x7fce2b672550>
I do think Python is superior in many, many, ways to all other languages, but as Python and JS skills are often desired in the same engineer, it seems that we're making it harder on the majority of the labor force.
"We" are making it harder? Who's "we"? Python predates JavaScript by a few years, and the latter language was spun up in less than two weeks in order to create an 'edge' in the browser wars. So I don't think anyone really planned for anyone to write multi-language code involving Python and JS.
ChrisA _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- Antoine Rozo
On 15 September 2017 at 03:38, Antoine Rozo <antoine.rozo@gmail.com> wrote:
Why is it ','.join(iterable), why isn't there join(',', iterable)
Because join apply on a string, and strings are defined by the str class, not by a specific protocol (unlike iterables).
Join actually used to only be available as a function (string.join in Python 2.x). However, nobody could ever remember whether the parameter order was "join this series of strings with this separator" or "use this separator to join this series of strings" - it didn't have the same kind of natural ordering that map and filter did thanks to the old "apply" builtin ("apply this function to these arguments" - while the apply builtin itself is no longer around, the "callable(*args, **kwds)" ordering that corresponds to the map() and filter() argument ordering lives on). This meant that when string.join became a builtin interface, it became a string method since: 1. Strings are one of the few Python APIs that *aren't* protocol centric - they're so heavily intertwined with the interpreter implementation, that most folks don't even try to emulate or even subclass them*. 2. As a string method, it's obvious what the right order has to be (separator first, since it's the separator that has the method) As a result, the non-obvious to learn, but easy to remember, method spelling became the only spelling when string.join was dropped for Python 3.0. Cheers, Nick. * One case where it was actually fairly common for folks to define their own str subclasses, rich filesystem path objects, finally gained its own protocol in Python 3.6 -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
I'm going to respond to a few replies with this one.
Because join apply on a string, and strings are defined by the str class, not by a specific protocol (unlike iterables).
Join actually used to only be available as a function (string.join in Python 2.x). However, nobody could ever remember whether the parameter order was "join this series of strings with this separator" or "use this separator to join this series of strings" - it didn't have the same kind of natural ordering that map and filter did thanks to the old "apply" builtin ("apply this function to these arguments" - while the apply builtin itself is no longer around, the "callable(*args, **kwds)" ordering that corresponds to the map() and filter() argument ordering lives on).
This meant that when string.join became a builtin interface, it became a string method since:
1. Strings are one of the few Python APIs that *aren't* protocol centric - they're so heavily intertwined with the interpreter implementation, that most folks don't even try to emulate or even subclass them*. 2. As a string method, it's obvious what the right order has to be (separator first, since it's the separator that has the method)
Your second point is aligned with my initial point. Providing it as a class method further removes the ambiguity of what the right order is because of there is only one parameter: the function. As for the comment about Python being better designed, I never implied it wasn't. Python is my favorite. However as someone who uses multiple languages, and JS has risen in popularity, and it's not going anywhere, I seek a more peaceful co-existence with this language - a language that was designed in 10 days in 1995. In a covert way, by embracing some of the not-too-terrible syntax you make it easier for JS programmers to feel at home in Python, and adopt it. I see this only has promoting a peaceful coexistence. Another pain point is python uses [].append() and JS uses [].join() Having a wrapper for append would be helpful. And for that matter, why isn't append/extend a global? I can add things to lots of different collections. lists, sets, strings...
And for that matter, why isn't append/extend a global? I can add things to lots of different collections. lists, sets, strings...
No, append is relevant when the collection is ordered. You update dictionnaries, you make unions of sets, and you append lists to another ones. These operations are specific for each datatype. And not all iterables need operations to append new elements. But you can chain generic iterables (with itertools.chain). 2017-09-15 16:21 GMT+02:00 Jason H <jhihn@gmx.com>:
I'm going to respond to a few replies with this one.
Because join apply on a string, and strings are defined by the str class, not by a specific protocol (unlike iterables).
Join actually used to only be available as a function (string.join in Python 2.x). However, nobody could ever remember whether the parameter order was "join this series of strings with this separator" or "use this separator to join this series of strings" - it didn't have the same kind of natural ordering that map and filter did thanks to the old "apply" builtin ("apply this function to these arguments" - while the apply builtin itself is no longer around, the "callable(*args, **kwds)" ordering that corresponds to the map() and filter() argument ordering lives on).
This meant that when string.join became a builtin interface, it became a string method since:
1. Strings are one of the few Python APIs that *aren't* protocol centric - they're so heavily intertwined with the interpreter implementation, that most folks don't even try to emulate or even subclass them*. 2. As a string method, it's obvious what the right order has to be (separator first, since it's the separator that has the method)
Your second point is aligned with my initial point. Providing it as a class method further removes the ambiguity of what the right order is because of there is only one parameter: the function.
As for the comment about Python being better designed, I never implied it wasn't. Python is my favorite. However as someone who uses multiple languages, and JS has risen in popularity, and it's not going anywhere, I seek a more peaceful co-existence with this language - a language that was designed in 10 days in 1995. In a covert way, by embracing some of the not-too-terrible syntax you make it easier for JS programmers to feel at home in Python, and adopt it. I see this only has promoting a peaceful coexistence.
Another pain point is python uses [].append() and JS uses [].join() Having a wrapper for append would be helpful.
And for that matter, why isn't append/extend a global? I can add things to lots of different collections. lists, sets, strings...
-- Antoine Rozo
On Sep 15, 2017 7:23 AM, "Jason H" <jhihn@gmx.com> wrote: Another pain point is python uses [].append() and JS uses [].join() Having a wrapper for append would be helpful. There should be one, and only one, obvious way to do it. And for that matter, why isn't append/extend a global? I can add things to lots of different collections. lists, sets, strings... This is a key misunderstanding. You CANNOT "append" to lots of collections. You can ADD to a set, which has a quite different semantics than appending (and hence a different name). You can neither add nor append to a string because they are immutable. I can imagine various collections where appending might make sense (mutable strings?). None of them are in builtins. If a 3rd party wrote such a collection with append semantics, they'd probably name it append... e.g. collections.deque has an "append" (but also an "appendleft").
Jason, I'm sorry if you feel that everyone is piling on you to poo-poo your ideas, but we've heard it all before. To you it might seem like "peaceful coexistence", but we already have peaceful coexistence. Python exists, and Javascript exists (to say nothing of thousands of other languages, good bad and indifferent), and we're hardly at war in any way. Rather, what it sounds like to us is "Hey, Python is really great, let's make it worse so Javascript coders won't have to learn anything new!" Um... what's in it for *us*? What benefit do we get? Honestly, there are so many differences between Python and Javascript that having to learn a handful more or less won't make any difference. The syntax is different, the keywords are different, the standard library is different, the semantics of code is different, the names of functions and objects are different, the methods are different, the idioms of what is considered best practice are different... Why, I could almost believe that Python and Javascript were different languages! -- Steve
Sent: Friday, September 15, 2017 at 12:24 PM From: "Steven D'Aprano" <steve@pearwood.info> To: python-ideas@python.org Subject: Re: [Python-ideas] Make map() better
Jason,
I'm sorry if you feel that everyone is piling on you to poo-poo your ideas, but we've heard it all before. To you it might seem like "peaceful coexistence", but we already have peaceful coexistence. Python exists, and Javascript exists (to say nothing of thousands of other languages, good bad and indifferent), and we're hardly at war in any way.
Rather, what it sounds like to us is "Hey, Python is really great, let's make it worse so Javascript coders won't have to learn anything new!"
Um... what's in it for *us*? What benefit do we get?
Honestly, there are so many differences between Python and Javascript that having to learn a handful more or less won't make any difference. The syntax is different, the keywords are different, the standard library is different, the semantics of code is different, the names of functions and objects are different, the methods are different, the idioms of what is considered best practice are different...
Why, I could almost believe that Python and Javascript were different languages!
Really, there is no offense taken. Clearly, I am a genius and you all are wrong. ;-) But really, I've learned a few things along the way. Though nothing to convince me that I'm wrong or it's a bad idea. It's just not liked by the greybeards, which I can appreciate. "here's some new punk kid, get off my lawn!" type of mentality. Dunning-Kruger, etc. Hyperbole aside, you (and others) raise some very good points. I think your question deserves an answer:
Um... what's in it for *us*? What benefit do we get?
How that question is answered, depends on who is 'us'? If you're a bunch of python purist greybeards, then conceivably nothing. But, according to [Uncle] Bob Martin, the software industry doubles in engineers every 5 years. meaning that they greybeards are asymptotically becoming a minority. The features I've requested are to ease newcomers who will undoubtedly have JS* experience. * I may have focused too much on JS specifically, however I split my time between C/C++/Java/Python/JS. These days it's mainly JS and Python. But the request to move map(), etc to member functions is just an OOP-motivated one. The C++ or Java developer in me also wants those things. So now we have engineers coming from 2 other languages in addition to JS that would benefit. Why should it be Python? I have to admit this isn't about fairness. Clearly Python's concepts are superior to JS, but the responsibility falls to Python because it's far more mutable than the other languages. The process to improve JS if fraught with limitations from the start. Some are technical limitations, some are managing-body imposed, and some are just market share (there must be more than one browser implementation). Again, Python wins. At the end of the day, I'm just asking for a the 1% of enhancements that will eliminate 98% of the frustration of polyglots. I can't say with any certainty how that shape the future, but I would expect that it would make Python even more popular while increasing productivity. I can't see that there would be any real downside. Except that there would be more than one way in some scenarios, but that additional way would be balanced with familiarity with other languages.
On 9/15/17 2:03 PM, Jason H wrote:
It's just not liked by the greybeards, which I can appreciate. "here's some new punk kid, get off my lawn!" type of mentality. Dunning-Kruger, etc.
I'm not sure if you meant this tongue-in-cheek or not. The main problem with your proposal is that it would break existing code. This seems like a really flip way to address that concern. I suspect even the hip young kids would like existing code to keep working. map() isn't even used much in Python, since most people prefer list comprehensions. There really are many differences between Python and Javascript, which go much deeper than the order of arguments to built-in functions. You are in a multi-lingual world. Accept it. --Ned.
On 09/15/2017 11:03 AM, Jason H wrote:
From: "Steven D'Aprano" <steve@pearwood.info>
Um... what's in it for *us*? What benefit do we get?
How that question is answered, depends on who is 'us'? If you're a bunch of python purist greybeards, then conceivably nothing.
How about refugees from other languages? Your suggestions only make Python worse -- hardly a commendable course of action.
The features I've requested are to ease newcomers who will undoubtedly have JS* experience.
The point of having different languages is to have different ways of doing things. Certain mindsets do better with Python's ways, others do better with JS's ways. That doesn't mean we should have the languages converge into the same thing. Aside: your suggestion that anyone who likes the philosophy of Python and doesn't want to incorporate features from other languages that are contrary to it is just old and set in their ways is offensive. Such assertions weaken your arguments and your reputation. -- ~Ethan~
On Fri, Sep 15, 2017 at 08:03:32PM +0200, Jason H wrote:
But really, I've learned a few things along the way. Though nothing to convince me that I'm wrong or it's a bad idea. It's just not liked by the greybeards, which I can appreciate. "here's some new punk kid, get off my lawn!" type of mentality. Dunning-Kruger, etc.
"The Dunning–Kruger effect is a cognitive bias wherein persons of low ability suffer from illusory superiority, mistakenly assessing their cognitive ability as greater than it is. The cognitive bias of illusory superiority derives from the metacognitive inability of low-ability persons to recognize their own ineptitude." -- Wikipedia. If you're attempting to win us over to your side, stating that we're too stupid to realise how dumb we are is NOT the right way to do so. But since you've just taken the gloves off, and flung them right in our faces, perhaps you ought to take a long hard look in the mirror. You've been told that at least some of the changes you're asking for are off the table because they will break working code. And that is still not enough to convince you that they are a bad idea? Breaking thousands of Python programs just so that people coming from Javascript have only 99 differences to learn instead of 100 is not an acceptable tradeoff. Adding bloat and redundant aliases to methods, especially using inappropriate names like "join" for append, increases the cognitive burden on ALL Python programmers. It won't even decrease the amount of differences Javascript coders have to learn, because they will still come across list.append in existing code. So these aliases will be nothing but lose-lose for everyone: - those who have to maintain and document them - those who have to learn them - those who have to answer the question "what's the difference between list.append and list.join, they seem to do the same thing" - those who have to read them in others' code - and those who have to decide each time whether to spell it alist.join or alist.append. I'm sorry that Python's OOP design is not pure enough for your taste, but we like it the way it is, and the popularity of the language demonstrates that so do millions of others. We're not going to ruin what makes Python great by trying to be poor clones of Java, C++ or Javascript, even if it decreases the learning time for people coming from Java, C++ or Javascript by one or two minutes. We're going to continue to prefer a mixed interface where functions are used for some operations and methods for others, and if that upsets you then you should read this: http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html -- Steve
On Wed, Sep 13, 2017 at 5:05 PM, Jason H <jhihn@gmx.com> wrote:
Python is weird in that there are these special magical globals that operate on many things.
Jason, that weirdness is actually a deep part of Python's philsophy. The language is very protocol driven. It's not just the built-in functions that use protocols; the language itself is built around them. For example, `for ... in ...` syntax loops over any iterable object. You can like it or leave it, but this will never change.
On Wed, Sep 13, 2017 at 05:09:37PM +0200, Jason H wrote:
The format of map seems off. Coming from JS, all the functions come second. I think this approach is superior.
Obviously Javascript has got it wrong. map() applies the function to the given values, so the function comes first. That matches normal English word order: * map function to values * apply polish to boots # not "apply boots from polish" * spread butter on bread # not "spread bread under butter" Its hard to even write the opposite order in English: map() takes the values and has the function applied to them which is a completely unnatural way of speaking or thinking about it (in English). I suggest you approach the Javascript developers and ask them to change the way they call map() to suit the way Python does it. After all, Python is the more popular language, and it is older too.
Also, how are we to tell what supports map()?
Um... is this a trick question? Any function that takes at least one argument is usable with map().
Any iterable should be able to map via: range(26).map(lambda x: chr(ord('a')+x)))
No, that would be silly. That means that every single iterable class is responsible for re-implementing map, instead of having a single implementation, namely the map() function. -- Steve
For these examples, you shouldn't be using map at all. On Wednesday, September 13, 2017 at 11:10:39 AM UTC-4, Jason H wrote:
The format of map seems off. Coming from JS, all the functions come second. I think this approach is superior.
Currently: map(lambda x: chr(ord('a')+x), range(26)) # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
But I think this reads better: map(range(26), lambda x: chr(ord('a')+x))
Currently that results in: TypeError: argument 2 to map() must support iteration
Also, how are we to tell what supports map()? Any iterable should be able to map via: range(26).map(lambda x: chr(ord('a')+x)))
While the line length is the same, I think the latter is much more readable, and the member method avoids parameter order confusion
For the global map(), having the iterable first also increases reliability because the lambda function is highly variable in length, where as parameter names are generally shorter than even the longest lambda expression.
More readable: IMHO: map(in, lambda x: chr(ord('a')+x)) out = map(out, lambda x: chr(ord('a')+x))
out = (chr(ord('a')+x) for x in out) is the most legible.
out = map(out, lambda x: chr(ord('a')+x))
Less readable (I have to parse the lambda): map(lambda x: chr(ord('a')+x), in) out = map(lambda x: chr(ord('a')+x), out) out = map(lambda x: chr(ord('a')+x), out)
But I contend: range(26).map(lambda x: chr(ord('a')+x))) is superior to all.
_______________________________________________ Python-ideas mailing list Python...@python.org <javascript:> https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
in that case you presented maybe it is more readeable but considering that lists as ["a","b"] can be inputted, not just using range, the result might be as it was. change x y people are happy change y and z people hate it Abdur-Rahmaan Janhangeer, Mauritius abdurrahmaanjanhangeer.wordpress.com On 13 Sep 2017 19:10, "Jason H" <jhihn@gmx.com> wrote:
The format of map seems off. Coming from JS, all the functions come second. I think this approach is superior.
Currently: map(lambda x: chr(ord('a')+x), range(26)) # ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
But I think this reads better: map(range(26), lambda x: chr(ord('a')+x))
Currently that results in: TypeError: argument 2 to map() must support iteration
Also, how are we to tell what supports map()? Any iterable should be able to map via: range(26).map(lambda x: chr(ord('a')+x)))
While the line length is the same, I think the latter is much more readable, and the member method avoids parameter order confusion
For the global map(), having the iterable first also increases reliability because the lambda function is highly variable in length, where as parameter names are generally shorter than even the longest lambda expression.
More readable: IMHO: map(in, lambda x: chr(ord('a')+x)) out = map(out, lambda x: chr(ord('a')+x)) out = map(out, lambda x: chr(ord('a')+x))
Less readable (I have to parse the lambda): map(lambda x: chr(ord('a')+x), in) out = map(lambda x: chr(ord('a')+x), out) out = map(lambda x: chr(ord('a')+x), out)
But I contend: range(26).map(lambda x: chr(ord('a')+x))) is superior to all.
_______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
participants (15)
-
Abdur-Rahmaan Janhangeer
-
Antoine Rozo
-
Chris Angelico
-
David Mertz
-
Edward Minnix
-
Ethan Furman
-
Jason H
-
Mark E. Haase
-
MRAB
-
Ned Batchelder
-
Neil Girdhar
-
Nick Coghlan
-
Nick Timkovich
-
Stefan Behnel
-
Steven D'Aprano