For you it may be fine to write:
List<string> strs = new List<string>();
And sure if you have been using C# for years you know all the things going on here.
But it shouldn’t be an argument that:
List<string> strs = [];
Is substentionally easier to grasp.
And that has been the theme of all changes.
The example you point out is the advanced case, someone only needs in a very specific case. It does not have a lot todo with learning the language.
The language design team is really making sure that the features work well throughout and I think that does deserve some credit.
I agree that = [] is perfectly fine syntax. But I would definitely argue that:
[with(capacity: values.Length * 2), ..
is non-intuitive and unnecessary. What other language is there that has this syntax? Alternatively, is this a natural way of writing this? I wouldn't say so.
My main language in my free time is Rust, a few years ago it was F#. So, I'm absolutely open to other syntax ideas. But I feel that there has to be a direction, things have to work together to make a language feel coherent.
Another example would be Clojure, which I started learning a few months ago (before we all got swept up in AI FOMO :D). Clojure as a language feels very coherent, very logical. I'm still a beginner, but every time I learn something about it, it just makes sense. It feels as if I could have guessed that it works this way. I don't get that feeling at all in many of the new features of C#.
> The example you point out is the advanced case, someone only needs in a very specific case. It does not have a lot todo with learning the language.
I disagree. When learning the language, you're going to have to read other people's code and understand it. It's the same basic principle, but, I'd argue, much worse in C++. Yes, in theory, you don't have to understand SFINAE and template metaprogramming and (now) concepts and all those things. You could just work in a subset of C++ that doesn't use those things. But in practice, you're always going to have issues if you don't.
> is non-intuitive and unnecessary.
intuitive is definitely in the eye of the beholder. When people saw:
`HashSet<string> people = [with(StringComparer.CaseInsensitiveComparer), .. group1, group2]`
they found it understandable. And this was also much nicer than what they'd have to write today (which would bring them out of the nice declarative collection-expression space).
Does that make it 'necessary'? Ultimately that's up to the individual. We felt like it was. Not being able to do simple things like this felt like a 'bitter pill'. Customization of collection construction is common (looking in codebases, it shows up about 7% of the time). So having to 'fall out' from the uniform collection-expr system into the much more verbose and clunky forms just for this common enough case felt 'necessary' to us.
>But I feel that there has to be a direction, things have to work together to make a language feel coherent.
I feel like this is conflicting feedback. Collection expressions made the language more coherent. Instead of 7 different ways of doing things (some of which were genuinely not efficient), we gave one uniform way of doing it. That makes things more coherent. Making it so you don't have to drop out of that for something as simple as configuring the collection makes things more coherent.
C# doesn't need to have syntax sugar for every possible use case.
Some of the more recent features feel like the outcome of the team pressure to have new language features to announce in November every year.
Collection expressions today are more the sort of thing that a code poet or golfer can do to prettify their code than something a newbie can count on using. It's tough to explain "you can only use this when the collection type is implied in that spot" to a newbie. The value of the base feature is still unproven for me. I'm not sure I agree, without some convincing, that collection expressions made the language more coherent rather than doing https://xkcd.com/927.
> Collection expressions made the language more coherent. Instead of 7 different ways of doing things (some of which were genuinely not efficient), we gave one uniform way of doing it.
I see your point on this. My dislike comes from a mixture of "I don't like how it looks" and "this language already has tons of features".
In terms of looks, I wish it could be more coherent with existing syntax.
List<int> = new {1, 2, 3} and List<int> = {1, 2, 3} are obviously taken up by anonymous types and blocks themselves. Would something like
List<int> = new(capacity: 10)[1, 2, 3]
have been possible? It feels like a combination of target-typed new and the initialization syntax. It involves the "new" keyword, which everybody already associates with constructor calls. It's short. Obviously, I don't know if this even works, maybe there's a parsing issue there (aren't those the most annoying issues in language design haha).
> they found it understandable
Kind of in my experience. Me and the people I've shown this to can easily remember it, but we all agree that it doesn't look like obvious syntax to them. Those two things are quite different to me. Contrast this to something like target-typed new, which immediately made sense to the same people. One might argue that that's fine enough and maybe it is, but I think, the less I have to remember about a language's syntax, the better. I'm going to have to remember many many other things anyway, better keep my memory free for the details of SynchronizationContext and async flow :)
I'm obviously aware that you get tons of bikeshedding comments like this all the time, so I'm sure you've gone through this. But to me, this invented syntax would have been fine. I just don't like the one that actually got in.
Now, the necessity on the other hand: May just be the company I'm working at, but my personal experience has never been that this is a big issue. Sure, it's nice to not have to fall back to explicit initialization a few more times. But personally, this doesn't pass my threshold of "painful enough to warrant additional syntax".
That's the core of my issue: Most, maybe all, of the new features in the language are fine to me in isolation. I may bikeshed about the explicit syntax (see: this thread). But my main issue is that the sum of complexity in the language and the issues beginners have when learning it are steadily increasing. I see this all the time at work.
As you said, this is definitely subjective. And in the end, language design is a very subjective process and maybe C# just won't be for me in the long run. But I wish it would, because at its core I like it, and .NET, a lot. Which is why I will continue to speak for my (subjective) viewpoint.
Well, this turned into a bit of an incoherent rant. I appreciate you exposing yourself to the HN acid pit ;)
https://docs.python.org/3/tutorial/datastructures.html#list-...
I'm also not sure that something not being intuitive or natural is necessarily a bad thing in of itself. You state it as if it's so, but you haven't demonstrated that this way of defining a list is worse. You also haven't made any attempt to understand any possible benefit, nor have you attempted any sort of analysis comparing the good and the bad aspects.
> I'm also not sure that something not being intuitive or natural is necessarily a bad thing in of itself. You state it as if it's so, but you haven't demonstrated that this way of defining a list is worse.
I would argue that a language having more features, without the feature being helpful, is a bad thing in itself. If the syntax isn't necessary or very convenient in many cases, it shouldn't exist. The syntax being natural (which, absolutely, is a very subjective thing) just makes it less of an issue, I'd say.
Every new syntax added to the language adds cognitive overhead to readers of code. But also, it adds possible interactions with other language features that may be added in the future. Now, the example I brought up doesn't really concern the second point, I'll concede that. But unions? That is a big concept to add to a language that already has decades of existing conventions and tons of other features. How will they interact with generics? Nullable reference types? And, just as importantly: How will they interact with any other features that might be added at some point that we don't even know about?
I'm not against adding syntax sugar. For example, I quite like primary constructors, which is another relatively new C# feature. I think it's a bit annoying that they were kind of added in a roundabout way, by first adding records and then adding primary constructors to classes, but this time they don't define properties but fields...but in the end, it's a nice convenience feature when using constructor injection. Which, whatever one may feel about this, is pretty common in C# code.
But the thing is: If every single feature that's nice for a few use cases gets added to a language, the language will explode. The best example for this is C++. C# is definitely not that bad, far from it, but my point is that I want it to stay that way :)
Dictionary<string, List<Tuple<string, string>>> foo = new Dictionary<string, List<Tuple<string, string>>>
Or things like: Dictionary<string, List<Tuple<string, string>>> foo = DoSomeWork();
And you'd have to get the type right, even though the compiler knew the type, because it'd tell you off for getting it wrong. Sometimes it was easiest to just grab the type from the compiler error. ( This example is of course a bit OTT, and it's a bit of a code-smell to be exposing that detail of typing to consumers. )No-one wants to go back to that, and anyone who says C# is over-complicated I think is forgetting how rough it was in the earliest versions.
While introduction of auto-typing through "var" helped a lot with that, you'd still regularly have to fight if you wanted to properly initialise arrays with values, because the syntax was just not always obvious.
Collection literals are amazing, and now the ability to pass things into the constructor means they can be used when you need constructor parameters too, that's just a good thing as you say.
This is exactly how C++ landed where it is now. Every time it's "you only need to know that syntax if..." well it ends up everyone has to know that syntax because someone will use it and if you're a responsible programmer you'll end up reading a lot code written from other people.
But still there is a difference between learning and mastering.
I recently helped my partner learn for her CS class, and I feel very comfortable arguing that my previous statement holds up.
Mastering? No, in that case I agree with you.
I work on multiple applications with different versions of C# and/or Dotnet. I find it quite annoying to have to remember what syntax sugar is allowed in which versions.
If C# did not want verbose syntax, then Java was a poor choice to imitate.
Without the lawsuit, COM+ Runtime (aka .NET) would have used J++, as originally designed in the Ext-VOS paper.