Which doesn't work, even in Python. Python 1.0 could iterate over various collections with straightforward syntax that still works. No such programmer would recognize the functional style that's taken over for iteration in the modern language.
Likewise the evolution from list comprehensions to generators left us with some terribly overlapping syntax.
Can't argue with that, but what I really said was that in contrast to the article, and Stroupstrup's rule, if I was to design a language I would put the "one way to do it" very high in my priorities.
Going with let's have "explicit syntax" now until the users get familiar with it and we will make it more terse later, is not a good idea IHMO.
But I do understand and appreciate that sometimes evolutionary changes will bring in duplication, so I am in no way attacking Rust and the specific example of the evolution of the error handling syntax.
> Going with let's have "explicit syntax" now until the
> users get familiar with it and we will make it more
> terse later is not a good idea IHMO.
I don't think this is a conscious decision. IMO, when you're first designing a language, you don't necessarily know what idioms are going to be most common, so you use special syntax sparingly, and force users to be explicit everywhere else. Then, once you have experience with how people use the language and can empirically observe what idioms are popular, you favor those idioms with shorthand syntax.
Getting back to the specific example in the OP, for a long time there was a discussion on whether Rust should use the question mark for some dedicated syntax (having removed the C-style ternary operator in 2012 as redundant with `if`). But people disagreed on what to use it for: some wanted to use it to designate functions that return booleans (like Ruby does); others wanted to use it to designate functions that return Option (my original stance, way back when); others wanted it as sugar for constructing and typing Options (as Swift eventually did); some wanted a C#-style coalescing operator. But after years of experience it turns out that none of the above are especially prevalent in Rust, especially once the community embraced the Result type (which matured relatively late in Rust's development) for most of what Option had originally been used for. In retrospect having a terse way to handle Results is an obvious win and I absolutely adore the new `?` operator, but it takes time to produce the evidence that such things are truly worth their weight.
I don't think you're wrong, I just think that you're overlooking that not all features eventually receive terse notation. Some stay loud and explicit forever! Only a few, relatively important features receive terse notation, and knowing which features those are requires observing how the language is used.
I think most languages go through a sort of growth phase (or multiple) where the users of the language grow and learn new patterns the language affords. As they learn, the style (idiomaticity??) of the language evolves. Expecting the language designers to anticipate this style from the outset seems almost impossible. I think the approach of starting with a possibly verbose base that provides the options needed and then boiling it down seems appropriate. But IANALD (language designer). :-)
Lets use Rust for an example. While I understand what a lot of operators in Rust do, like let X : Y = Z or !foo, I personally adore the Python habit of starting with a word based grammar and graduating common functionality into more obscure abbreviations (def) or glyphs (%).
Using those examples, let X as Y = Z is just one more letter but eliminates any confusion for someone unfamiliar with the syntax, since its fairly distinct from other C like languages.
Its also not particularly cumbersome for a language to support a ! operator and the keyword "not" for logical negation.
Same with : and "in" in for loops.
That and syntax features like whitespace significance as a basis are IMO great for good style.
For a language like Rust where it is now, adding these optionally would greatly increase my enjoyment of the language, because I generally like programming that I can read like Python, despite having a half decade of C++ experience so its not for a lack of benefit from the terseness.
Whenever I come across someone whose favorite language is Python, their argument is usually that "my pseudocode is basically Python!". While Python is certainly not my favorite language I really adore its syntax. A joy to work with. The wordiness and significant whitespace are features to me. For any feature, the syntax is usually the simplest and most obvious thing you can think of. No reason to look things up; guessing is actually a viable alternative! It's the lack of compile-time checks, support for functional programming, run-time weirdness and slowness that I don't like. It's an excellent replacement for shellscript, though.
Ruby is even better as a shell script replacement. It has things like backtics for command execution, built-in regex operators, string interpolation, and other nice shortcuts. Python is a bit too rigid/dogmatic.
Now Python has asynchronous list comprehensions, for which there seem to be few use cases. If Python supported compute parallelism it might be useful, but Python async is only for async I/O.
>There should be one-- and preferably only one --obvious way to do it.
The zen of python isn't saying "only have one way to do anything" it says "have at least one obvious way, and preferably only one obvious way to do this".
There can be lots of ways to iterate over a list:
[x for x in l]
for x in l:
x
while (x for x in [1,2,3]).next():
...
Maybe languages should provide starter pack, not batteries (as in libraries made noob friendly). Just enough vocabulary in a module to go along with most needs.