It surprises me how much his considerations match my world view pre-Go, and in a sense give me a fulfilling explanation about why I got hooked into the language. I still recall sitting in a hotel years ago with Jamu Kakar while we went through the upcoming C++0x standard (now C++11) and got perplexed about how someone could think that having details such as rvalue references and move constructors into the language specification was something reasonable.
Rob also expressed again the initial surprise that developers using languages such as Python and Ruby were more often the ones willing to migrate towards Go, rather than ones using C++, with some reasonable explanations about why that is so. While I agree with his considerations, I see Python going through the same kind of issue that caused C++ to be what it is today.
Consider this excerpt from PEP 0380 as evidence:
If yielding of values is the only concern, this can be performed without much difficulty using a loop such asfor v in g: yield v
However, if the subgenerator is to interact properly with the caller in the case of calls to send(), throw() and close(), things become considerably more difficult. As will be seen later, the necessary code is very complicated, and it is tricky to handle all the corner cases correctly.
A new syntax will be proposed to address this issue. In the simplest use cases, it will be equivalent to the above for-loop, but it will also handle the full range of generator behaviour, and allow generator code to be refactored in a simple and straightforward way.
This description has the same DNA that creates the C++ problem Rob talks about. Don’t get me wrong, I’m sure yield from will make a lot of people very happy, and that’s exactly the tricky part. It’s easy and satisfying to please a selection of users, but often that leads to isolated solutions that create new cognitive load and new corner cases that in turn lead to new requirements.
The history of generators in Python is specially telling:
- PEP 0234 [30-Jan-2001] – Iterators – Accepted
- PEP 0255 [18-May-2001] – Simple Generators – Accepted
- PEP 0288 [21-Mar-2002] – Generators Attributes and Exceptions – Withdrawn
- PEP 0289 [30-Jan-2002] – Generator Expressions – Accepted
- PEP 0325 [25-Aug-2003] – Resource-Release Support for Generators – Rejected
- PEP 0342 [10-May-2005] – Coroutines via Enhanced Generators – Accepted
- PEP 0380 [13-Feb-2009] – Syntax for Delegating to a Subgenerator – Accepted
You see the rabbit hole getting deeper? I’ll clarify it further by rephrasing the previous quote from PEP 0380:
If [feature from PEP 0255] is the only concern, this can be performed without much difficulty using a loop […] However, if the subgenerator is to interact properly with [changes from PEP 0342] things become considerably more difficult. [So we need feature from PEP 0380.]
Yet, while the language grows handling self-inflicted micro-problems, the real issue is still not solved. All of these features are simplistic forms of concurrency and communication, that don’t satisfy the developers, causing community fragmentation.
This happened to C++, to Python, and to many other languages. Go seems slightly special in that regard in the sense that its core development team has an outstanding respect for simplicity, yet dares to solve the difficult problems at their root, while keeping these solutions orthogonal so that they support each other. Less is more, and is not always straightforward.