this post was submitted on 20 Oct 2023
48 points (94.4% liked)

Programming

17369 readers
450 users here now

Welcome to the main community in programming.dev! Feel free to post anything relating to programming here!

Cross posting is strongly encouraged in the instance. If you feel your post or another person's post makes sense in another community cross post into it.

Hope you enjoy the instance!

Rules

Rules

  • Follow the programming.dev instance rules
  • Keep content related to programming in some way
  • If you're posting long videos try to add in some form of tldr for those who don't want to watch videos

Wormhole

Follow the wormhole through a path of communities [email protected]



founded 1 year ago
MODERATORS
all 21 comments
sorted by: hot top controversial new old
[–] douglasg14b 11 points 1 year ago* (last edited 1 year ago) (2 children)

I do feel like C# saw C++ and said "let's do that" in a way.

One of the biggest selling points about the language is the long-term and cross repo/product/company..etc consistency. Largely the language will be very recognizable regardless of where it's written and by who it's written due to well established conventions.

More and more ways to do the same thing but in slightly different ways is nice for the sake of choices but it's also making the language less consistent and portable.

While at the same time important language features like discriminated unions are still missing. Things that other languages have started to build features for by default. C# is incredibly "clunky" in comparison to say Typescript solely from a type system perspective. The .Net ecosystem of course more than makes up for any of this difference, but it's definitely not as enjoyable to work with the language itself.

[–] [email protected] 8 points 1 year ago* (last edited 1 year ago) (1 children)

I certainly see where you’re coming from, but I think the designers of C# have done fairly good job evolving the language to balance backwards compatibility, simplicity (in terms of having “only one way” to do things), and the ergonomics expected of modern languages. I think C++ and JS are great comparisons because C++ has at this point added everything and the kitchen sink to it’s language and standard library, whereas C# has gone much more like JS introducing features that evolve the best practices for writing but still feel and read like essentially the same language. For example, primary constructors still look just like regular C#, it’s just a nicer way to define simple POCOs when desired.

As far as important language features, I think it’s easy to pick on discriminated unions because it seems like C#’s users unanimously want that. However, if you read through proposals and discussions, it’s obvious that there’s a lot of nuance and trade offs in deciding how and what form of discriminated unions should exist in C# (and the designers are very active in working through that nuance and trade off - they said they have a working group that meets weekly to discuss it I believe*). And to be fair, they have introduced a LOT of other important features (like records and the vastly improved pattern matching) in just the last few years. Without those features, discriminated unions wouldn’t be nearly as appealing, and those features are great for the language even without DUs.

*Edit: Source for my claim is the recent Languages & Runtime Community Standup on the official dotnet YouTube channel. Mads talks about the working group at 21:05, but the discussion of discriminated unions begins at 7:09.

[–] Walnut356 4 points 1 year ago* (last edited 1 year ago) (1 children)

the ergonomics expected of modern languages.

As someone learning c# right now, can we get some of those "modern ergonomics" for switch statements 💀

I cant believe it works the way it does. "Fallthrough logic is a dumb footgun, so those have to be explicit rather than the default. But C programmers might get confused somehow, so break has to be explicit too"

I miss fallthrough logic in languages that dont have it, and the "goto case" feature is really sick but like... Cmon, there's clearly a correct way here and it isnt "there is no default behavior"

[–] [email protected] 1 points 1 year ago* (last edited 1 year ago) (1 children)

I’m not sure I understand your point about fall through having to be explicit, but I agree that switch statements are lacking ergonomics - which makes some sense considering they were added a looooong time ago. Luckily, they added recently the switch expression, which uses pattern matching and behaves more like Rust’s Match expression. It’s still lacking proper exhuastiveness checks for now, but that’s a problem with the core design of composition in C#’s type model and one they are looking to solve (alongside Discriminate Unions in all likelihood).

[–] Walnut356 2 points 1 year ago* (last edited 1 year ago) (2 children)

I’m not sure I understand your point about fall through having to be explicit

As far as i understand it, every switch statement requires a break otherwise it's a compiler error - which makes sense from the "fallthrough is a footgun" C perspective. But fallthrough isnt the implicit behavior in C# like it is in C - the absence of a break wouldnt fall through, even if it wasnt a compiler error. Fallthrough only happens when you explicitly use goto.

But break is what you want 99% of the time, and fallthrough is explicit. So why does break also need to be explicit? Why isnt it just the default behavior when there's nothing at the end of the case?

It's like saying "my hammer that's on fire isnt safe, so you're required to wear oven mitts when hammering" instead of just... producing a hammer that's not on fire.

From what i saw on the internet, the justification (from MS) was literally "c programmers will be confused if they dont have to put breaks at the end".

[–] [email protected] 1 points 1 year ago (1 children)

Ah ok I think I get you now. To be clear, fall through is implicit - when the case being fallen through is empty. I forgot that, if you want to execute some statements in one case, and then go to another case, you need gotos. To be fair, I’ve never needed that behavior before.

I absolutely see your point on break not being the default. It is sad, although I will say I don’t mind a little extra explicitness in code I’m sharing with a large team.

[–] Walnut356 1 points 1 year ago

To be clear, fall through is implicit - when the case being fallen through is empty

That's even worse... why isnt an empty case a syntax error?

[–] [email protected] 1 points 1 year ago* (last edited 1 year ago)

I mean if your issue is with the break pattern you could just switch to using a switch expression instead of a statement, it'll probably be a little cleaner for ya

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/switch-expression

Edit: sorry just realized y'all already talked about this :p

[–] [email protected] 1 points 1 year ago

I think one of the things holding back some of the more impactful features we could see in C# is the need to also update the CLR in many cases to handle things like new kinds of types, new kinds of expressions, etc. TypeScript has the benefit of being executed by a dynamic runtime, but C#'s runtime is unfortunately statically typed, meaning it also needs to be updated with the language. It's also used by multiple languages, for what it's worth.

That being said, if they redirected some of their efforts towards improving the CLR as well, I think they could put out all the cool features they've mostly sidelined, like DUs and some form of their extension everything proposal.

[–] [email protected] 6 points 1 year ago

Lol I typically learn about new c# features through resharper recommendations and refactorings

[–] [email protected] 5 points 1 year ago

Nice, primary constructors are one of my favorite little QoL features of writing objects in F#. Cool to see it come to C#.

[–] KindaABigDyl 4 points 1 year ago (1 children)

I need to get caught up on C#. I stopped using it just before C# 8

[–] douglasg14b 8 points 1 year ago* (last edited 1 year ago) (1 children)

The great thing about languages like C# is that you really don't need to "catch up". It's incredibly stable and what you know about C#8 (Really could get away with C# 6 or earlier) is more than enough to get you through the grand majority of personal and enterprise programming needs for the next 5-10 years.

New language versions are adding features, improving existing ones, and improving on the ergonomics. Not necessarily breaking or changing anything before it.

That's one of the major selling points really, stability and longevity. Without sacrificing performance, features, or innovation.

[–] KindaABigDyl 1 points 1 year ago

Sure, but from what I've heard, I'm pretty sure there are a lot of new features that I will want to use :)

[–] [email protected] 3 points 1 year ago (3 children)

I'm sure a lot of work went into this, but it seems crazy that we need another way to initialise collections, and another way to make fixed sized arrays.

I just want less boilerplate.

[–] Lucky 3 points 1 year ago (1 children)

The new list initializing syntax is less boilerplate, no?

[–] [email protected] 0 points 1 year ago (2 children)

Look how clunky it is to add support on a collection. Take the example here:

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/collection-expressions#collection-builder

It involves adding a new builder class, and they actually have to reference the function by string name.

It's actually making me even more angry now that I look at it in detail.

[–] Lucky 2 points 1 year ago

This is for custom collections, right? And you don't even have to use it, you can keep using existing ctors for your custom collections

Worse case scenario you keep doing what we've always had to do. But for the 99% of use cases we get a much more streamlined initializer, with extensions to use our own.

I don't see how that's a bad thing

[–] [email protected] 1 points 1 year ago

they actually have to reference the function by string name.

This is true of a lot of the opt-in language features though, isn't it? For example, you can just make an .Add method on any IEnumerable type and get collection initializer syntax supported for it, even as an extension method. The same works for Dispose on ref structs I believe, and I remember there being a few other places where this was true (GetAwaiter I think?).

[–] eluvatar 1 points 1 year ago

I think for arrays it's not really a benefit. But for other types it's superior, compared to the current syntax which just calls add on the list object, which is very inefficient compared to building the whole thing at once which is what this new syntax does.