On Wed, 13 Oct 2021 at 18:30, Chris Angelico firstname.lastname@example.org wrote:
On Thu, Oct 14, 2021 at 1:36 AM Oscar Benjamin email@example.com wrote:
Your suggestion is that this is a bug in map() which is a fair alternative view. Following through to its conclusion your suggestion is that every possible function like map, filter, and all the iterator implementations in itertools and in the wild should carefully wrap any internal non-next function call in try/except to change any potential StopIteration into a different exception type.
Yes, because it is the map function that is leaking StopIteration.
But it is not the map function that *raises* StopIteration. The exception "leaks" through map just like *all* exceptions "leak" through *all* Python functions in the absence of try/except. This is not normally referred to as "leaking" but rather as "propagating" and it is precisely the design of exceptions that they should propagate to the calling frame. The difference in the case of StopIteration is that it can be caught even if there is no try/except.
My view is that it would be better to have a basic primitive for getting an element from an iterable or for advancing an iterator that does not raise StopIteration in the first place. I would probably call that function something like "take" rather than "first" though. The reason I prefer introducing an alternative to next() is because I think that if both primitives were available then in the majority of situations next() would not be the preferred option.
How will that solve anything though? You still need a way to advance an iterator and get a value from it, or get told that there is no such value. No matter what exception you choose, it will ALWAYS be possible for the same problem to occur. Exceptions like ValueError will, instead of early-aborting a map(), cause something to mistakenly think that it couldn't parse a number, or something like that.
I find it surreal that I am arguing that StopIteration is a uniquely problematic exception and that you seem to be arguing that it is not. Yet at the same time you are an author of a (successful!) PEP that was *entirely* about this very subject: https://www.python.org/dev/peps/pep-0479/
The first two paragraphs of the rationale from the PEP: """ The interaction of generators and StopIteration is currently somewhat surprising, and can conceal obscure bugs. An unexpected exception should not result in subtly altered behaviour, but should cause a noisy and easily-debugged traceback. Currently, StopIteration raised accidentally inside a generator function will be interpreted as the end of the iteration by the loop construct driving the generator.
The main goal of the proposal is to ease debugging in the situation where an unguarded next() call (perhaps several stack frames deep) raises StopIteration and causes the iteration controlled by the generator to terminate silently. (Whereas, when some other exception is raised, a traceback is printed pinpointing the cause of the problem.) """ I agree entirely with the above but every occurence of "generators" should have been generalised to "iterators" in order to address the problem fully.
You think this should be fixed in map. I think that the root of the problem is next. The PEP discusses changing next: https://www.python.org/dev/peps/pep-0479/#converting-the-exception-inside-ne... The idea was rejected on backward compatibility grounds: I am proposing that an alternative function could be added which unlike changing next would not cause compatibility problems.
Although we may disagree about what is the best way to fix this I don't see how we can disagree that StopIteration is a uniquely problematic exception to raise (as you seem to argue above).