[HN Gopher] Official proposal for Type Unions in C#
       ___________________________________________________________________
        
       Official proposal for Type Unions in C#
        
       Author : Fervicus
       Score  : 198 points
       Date   : 2024-08-07 17:02 UTC (5 hours ago)
        
 (HTM) web link (github.com)
 (TXT) w3m dump (github.com)
        
       | zamalek wrote:
       | > The interior layout of the union struct is chosen to allow for
       | efficient storage of the data found within the different possible
       | member types with tradeoffs between speed and size chosen by the
       | compiler.
       | 
       | Having _attempted_ (and being bitten by) far too much black magic
       | with C# unions in the past (using FieldOffset), there is an
       | unfortunate situation here: aliasing a pointer/ref value and
       | value is UB. This means that a struct union of a u64 and an
       | object would need separate fields for each, wasting 8 bytes. That
       | is, unless the ryujit/gc is updated with knowledge about this.
        
       | tombert wrote:
       | Huh, I did F# for years with discriminated unions, and I guess I
       | just assumed C# would have had them by now.
       | 
       | I know not everyone likes them, but for typed languages I find it
       | extremely hard to go back to languages without ADTs of some kind.
       | I do Java for my current job, and Java is generally fine enough,
       | but it's a little annoying when I have to do whacky workarounds
       | with wrapper classes to get something that would be done in three
       | lines of F#.
        
         | Eji1700 wrote:
         | F# is so hard to walk back from. I wish Microsoft would support
         | it better and actually push it, because it's such a perfect
         | sweet spot. Most of the functional advantages without being
         | shackled to pure functions and the like is so easy to develop
         | in.
         | 
         | Instead they've been, very slowly, turning C# into F#, which is
         | even weirder to watch.
        
           | ctenb wrote:
           | It makes sense from a language adoption standpoint,
           | especially if you look at typescript for example, which is
           | also by Microsoft. Though I agree with you personally
        
           | tombert wrote:
           | I agree; F# is probably my favorite of the "compromise"
           | functional languages [1], where you can drop into the "wrong"
           | way when necessary. F# pushes a more or less pure approach,
           | but in some cases, like when mutation will be a bit easier
           | and/or faster, it's easy to do that as well. I also think
           | that even F#'s OOP is actually really pleasant...If nothing
           | else, it's a lot more terse than C#'s.
           | 
           | I miss writing it; I haven't had a job using it in a few
           | years but I really enjoyed writing code in it when I did.
           | Most of my personal projects haven't really been able to use
           | .NET for awhile, so I never seem to have an excuse to play
           | with F# in my personal time.
           | 
           | I never got into C#, so I can't say that I really feel the
           | pain of the C# transition into F#.
           | 
           | [1] At least in the typed world. I'm also really partial to
           | Clojure.
        
           | kriiuuu wrote:
           | I feel the same about Scala. I use Scala3 daily and almost
           | every other language is such a step back. I have looked at F#
           | and it looks like one of the only other languages I would
           | enjoy as much as Scala. Haskell is nice too, but the
           | ecosystem is just not quite there. F# being able to tap into
           | the rich C# ecosystem and Scala being able to tap into the
           | Java ecosystem is such a win and makes them feel a lot less
           | niche when you use them.
        
             | tombert wrote:
             | I do like Scala, and I haven't touched Scala3, but I've
             | found it pretty unwieldy for actual use. It feels like I'm
             | constantly fighting with the type system in a way that ends
             | up being annoying an unpleasant.
             | 
             | To be clear, it's not like I'm new to functional languages
             | and their type systems, I've been doing Haskell for
             | forever, and I did F# for a multiple years, but Scala has
             | never really clicked for me.
             | 
             | Personally, for doing JVM stuff, I'm a pretty die-hard
             | Clojure advocate. I feel like it's easy for me to write
             | Clojure in the way that I think, while still allowing me to
             | mooch off the entire Java ecosystem, and if there are
             | things that I don't like about the language it's really not
             | _that_ hard to macro in a new feature (though obviously
             | this should be used sparingly).
             | 
             | I definitely recommend giving F# a try. I think it's an
             | extremely underrated language.
        
               | agent281 wrote:
               | > I definitely recommend giving F# a try. I think it's an
               | extremely underrated language.
               | 
               | Do you have any recommendations for people who do not
               | have C#/.NET experience who want to rip their toes in F#?
               | The last time I tried the language I bumped my head on
               | the .NET parts. :/
        
               | tombert wrote:
               | Out of curiosity, what confused you about the .NET stuff?
               | I didn't have any .NET experience going into F# either
               | [1] and I didn't find it too hard to pick up the .NET
               | stuff.
               | 
               | For the most part I didn't need most of the built in .NET
               | libraries; the F# stuff was fine, but the ones I ended up
               | using a lot were the threadsafe libraries for when I
               | needed mutation across threads. ConcurrentDictionary was
               | one I used an awful lot, ConcurrentBag occasionally,
               | SemaphorSlim, and Interlocked Incrementors.
               | 
               | The reason that the .NET compatibility was handy to me
               | was because it made it easy to get third party libraries
               | without much fuss, which was great because they were
               | generally pretty well supported.
               | 
               | The rule of thumb that I used was that F#, while it kind
               | of looks like Haskell, is not Haskell, and specifically
               | it's not lazy, and as such I always kind of pretended I
               | was in the `do` block. Things execute top down, and you
               | can mix in side effects wherever, so you kind of have to
               | pretend everything is in an IO monad, or at least if you
               | see anything that has a side effect in there.
               | 
               | [1] I was hired at Jet.com specifically because I had
               | Haskell and Erlang experience prior. I had never written
               | any significant C# code before.
        
               | agent281 wrote:
               | I was working on a Linux laptop and the installation
               | process wasn't immediately clear. Some of the frameworks
               | I looked at didn't target the most recent .NET version so
               | I had to install multiple versions. Once I had it
               | installed, I had compilation failures with a project that
               | was built on Windows. The team that ran it wasn't sure
               | what was happening.
               | 
               | Also, having the documentation split between the F# parts
               | on one site and the .NET parts on another site was a bit
               | of a pain.
               | 
               | It might have gone better if I was starting with a book
               | instead of picking docs off the web.
        
               | tombert wrote:
               | Ah, yeah, the non-windows stuff was pretty clunky for
               | awhile, but I do feel like it's gotten a bit better at
               | the command line support.
               | 
               | FWIW I think it works fine with Nix, so if you're not
               | opposed to using that package manager it should pretty
               | trivial to get different versions working with a nix
               | shell and/or flake.
        
               | progmetaldev wrote:
               | Hopefully this website can get you further with the
               | installation. There will probably still be issues between
               | Windows and Linux specific tutorials, depending on what
               | you are trying to do.
               | 
               | https://fsharp.org/use/linux/
        
               | pjmlp wrote:
               | It is a dream learning guest languages, while trying to
               | avoid the platform, one really needs to understand UNIX,
               | Web, .NET, Java, Erlang... for using whatever alternative
               | leaky abstractions are provided on top of them.
               | 
               | In F#'s case, there are plenty of good books, and then it
               | is really needed to learn the platform.
               | 
               | Some well known ones => Stylish F#, F# in Action, Domain
               | Modeling Made Functional
        
               | progmetaldev wrote:
               | Thank you for the recommendations. I still find using a
               | physical book easier to learn from, even if they do tend
               | to be out of date faster than online resources. I feel as
               | though F# doesn't move as fast as other parts of the .NET
               | ecosystem, and in particular the Domain Modeling Made
               | Functional seems like it will be relevant for years to
               | come.
        
               | progmetaldev wrote:
               | While a bit dated, F# For Fun and Profit is a great
               | learning resource:
               | 
               | https://fsharpforfunandprofit.com/
        
           | electroly wrote:
           | I'm glad they're simply turning C# into F#. They're coming to
           | us where we are, rather than forcing us to make a jump. I'm a
           | relatively "blue collar" developer, I know a little about FP
           | (not enough to be productive in F#) but I have tons of C#
           | experience. I appreciate them taking the good stuff from F#
           | and making it understandable to C# developers. It's been a
           | delightful progression; no big jumps, no paradigm shifts,
           | just the steady addition of useful new features that I can
           | understand.
        
             | Eji1700 wrote:
             | So the issue for those of us in F# land, is that it's
             | really not a big jump from C#. I've taught it (lightly) to
             | at least one C# dev who basically picked it up in a
             | weekend.
             | 
             | Something that helps, a ton, is as you're transitioning,
             | you can write code almost identically to how you did
             | before. It doesn't force FP on you at all. It's less useful
             | if you try to do that, because you're not taking advantage
             | of the language, but it makes testing your beliefs on how
             | things work SUPER easy because you can set it up in the OOP
             | way, get the result you expect, then set up in what you
             | think is the correct FP way, and see if it matches.
             | 
             | The only issue is there's a severe lack of beginner
             | friendly documentation (although it's leagues better than
             | it used to be). I honestly think that if you're a C# dev,
             | it's probably worth giving it a weekend or two to play
             | with. ESPECIALLY if you like linq syntax.
        
             | progmetaldev wrote:
             | I agree with you, as far as adding new features to C# from
             | F#. When I have the time to refactor larger codebases, I'm
             | often finding new C# features/syntax makes the code easier
             | to understand and reason about (often due to decreasing the
             | number of lines needed to accomplish the same thing,
             | without overcomplicating things). I do make sure that if
             | I'm introducing new language features, that I'm refactoring
             | related portions of code, so I'm not leaving behind the
             | "old" way of doing things while also introducing the "new"
             | way for the same functionality. I'm always thinking of the
             | next developer with anger issues coming into the codebase
             | after me.
        
         | kriiuuu wrote:
         | Good news is that you can achieve this with relatively little
         | boilerplate in Java with sealed interfaces and records now.
         | Relative to Java that is
        
           | tombert wrote:
           | Yeah, I know, and I have started using those, though that
           | doesn't undo the last 15 years of Java code that I've written
           | where that wasn't really an option.
           | 
           | Still, good to see Java joining the 21st century at least.
        
         | Arnavion wrote:
         | As of 2020 (when I stopped using F# and .Net in general), the
         | CLR only had inheritance and F#'s enums were actually
         | implemented using inheritance. Eg Some and None were derived
         | classes of Option class. You would see it if you inspected an
         | F# assembly in a decompiler. I don't know if it's still the
         | same today. This C# proposal has anonymous enums using `A or B`
         | syntax which would be hard to make work as sugar over
         | inheritance, so I guess the CLR would support enums first-class
         | for this to work (if it doesn't already).
        
       | arwhatever wrote:
       | I've lost track of all of the red/blue/white/black pill color
       | metaphors, but unions with exhaustive pattern matching is one of
       | the toughest of all programming language features to live without
       | once you become aware of it.
       | 
       | I've never felt like I've fully understood the implications of
       | the expression problem
       | https://en.wikipedia.org/wiki/Expression_problem but my
       | best/latest personal hypothesis is that providing extension
       | points via conventional polymorphism might be best suited for
       | unknown/future clients who might extend the code, but unions with
       | exhaustive pattern matching seem better suited for code that I or
       | my team owns. I don't typically want to extend that code. More
       | often, I instead want to update my core data structures to
       | reflect ongoing changes in my understanding of the business
       | domain, more often than not using these Union-type relationships,
       | and then lean on the compiler errors maximally to get feedback
       | about where the rest of the imperative code now no longer matches
       | the business domain data structures.
        
         | peheje wrote:
         | Maybe I'm off, but to me the gist of the expression problem can
         | be explained by contrasting how code extensibility is achieved
         | in OOP/FP.
         | 
         | OOP Approach with interface/inheritance:
         | 
         | Easy: Adding new types (variants) of a base class/interface.
         | Hard: Adding new functionality to the base class/interface, as
         | it requires implementing it in all existing types.
         | 
         | FP Approach with Discriminated Unions:
         | 
         | Easy: Adding new functions. Create a function and match on the
         | DU; the compiler ensures all cases are handled. Hard: Adding
         | new types to the DU, as it requires updating all existing
         | exhaustive pattern matches throughout the codebase.
         | 
         | Here's some Kotlin code. Kotlin is great because it can do both
         | really well.                 // Object-Oriented Approach
         | interface Shape {           fun area(): Double           fun
         | perimeter(): Double       }            class Circle(val radius:
         | Double) : Shape {           override fun area() = Math.PI \*
         | radius \* radius           override fun perimeter() = 2 \*
         | Math.PI \* radius       }            class Rectangle(val width:
         | Double, val height: Double) : Shape {           override fun
         | area() = width \* height           override fun perimeter() = 2
         | \* (width + height)       }            // Easy to add new shape
         | class Triangle(val a: Double, val b: Double, val c: Double) :
         | Shape {           override fun area(): Double {
         | val s = (a + b + c) / 2               return Math.sqrt(s \* (s
         | - a) \* (s - b) \* (s - c))           }           override fun
         | perimeter() = a + b + c       }            // Hard to add new
         | function (need to modify all existing shapes)       //
         | interface Shape {       //     fun area(): Double       //
         | fun perimeter(): Double       //     fun draw(): String  // New
         | function       // }            // Functional Approach
         | sealed class ShapeFP {           data class CircleFP(val
         | radius: Double) : ShapeFP()           data class
         | RectangleFP(val width: Double, val height: Double) : ShapeFP()
         | }            fun area(shape: ShapeFP): Double = when (shape) {
         | is ShapeFP.CircleFP -> Math.PI \* shape.radius \* shape.radius
         | is ShapeFP.RectangleFP -> shape.width \* shape.height       }
         | fun perimeter(shape: ShapeFP): Double = when (shape) {
         | is ShapeFP.CircleFP -> 2 \* Math.PI \* shape.radius
         | is ShapeFP.RectangleFP -> 2 \* (shape.width + shape.height)
         | }            // Easy to add new function       fun draw(shape:
         | ShapeFP): String = when (shape) {           is ShapeFP.CircleFP
         | -> "O"           is ShapeFP.RectangleFP -> "[]"       }
         | // Hard to add new shape (need to update all existing
         | functions)       // sealed class ShapeFP {       //     data
         | class CircleFP(val radius: Double) : ShapeFP()       //
         | data class RectangleFP(val width: Double, val height: Double) :
         | ShapeFP()       //     data class TriangleFP(val a: Double, val
         | b: Double, val c: Double) : ShapeFP()       // }
        
           | arwhatever wrote:
           | I think you've nailed the definitions.
           | 
           | However the definitions make the 2 choices (adding new types
           | vs adding new functions/operations) sound like a toss-up.
           | 
           | It is the fact that I tend to find the FP/DU approach so much
           | more frequently useful for my/my team's own code that makes
           | me wonder if I'm missing something.
           | 
           | Perhaps the important distinction I've been missing is in
           | Wikipedia's definition:
           | 
           | "The goal is to define a data abstraction that is extensible
           | both in its representations and its behaviors, where one can
           | add new representations and new behaviors to the data
           | abstraction, without recompiling existing code, and while
           | retaining static type safety (e.g., no casts)."
           | 
           | ... but when I'm working on my own/team's code, it is
           | perfectly sensible to recompile the code constantly.
        
             | jerf wrote:
             | The reason why it matters less than people intuitively
             | think is precisely that it matters a lot less when you're
             | in full control of both the operations and the types
             | anyhow, and that's actually the most common case. Generally
             | you are "composing" in library code, that is, just _using_
             | it, not extending the library itself.
             | 
             | When you are extending, you actually want to choose the
             | correct thing depending on what you need. Going the wrong
             | direction is painful in both directions.
             | 
             | Personally I think one of the reasons sum types are greeted
             | with such "oh my gosh where have you been all my life"
             | reactions is precisely that we had type extension as our
             | only option for so long. If we had had only sum types, and
             | type extension was given to us for the first time in
             | obscure languages 20 years ago and they only really started
             | getting popular in the last 5 or so, I think they'd be
             | considered in much the same way. Just as in a world with
             | only screwdrivers, the invention of the hammer would be
             | hailed as a revolution... and in a world with only hammers,
             | the invention of the screwdriver would be hailed as a
             | revolution. But in both cases the real mystery is how the
             | hypothetical world got that far in the first place.
             | 
             | Not that they aren't useful; consider what it means that
             | I'm analogizing them to something like a hammer and a
             | screwdriver, not an oil filter remover or something. It is
             | weird that we were missing one of them for as long as we
             | were in the mainstream.
        
               | arwhatever wrote:
               | Sum types are not a new discovery for me, personally.
        
       | ComputerGuru wrote:
       | Every time the question of preferred programming languages comes
       | up, I'm usually in the extreme minority with my preferences being
       | Rust (for small/fast) and C# (for productive and easy, where GC
       | is acceptable), a combination that doesn't seem to appeal to too
       | many people.
       | 
       | But for the projects that fall right about in the middle where it
       | could go either way and I could see either language working, I
       | almost always pick rust with discriminated unions being the
       | biggest reason. It's _incredibly hard_ to go back to a language
       | without even basic ADTs after seeing the light.
        
         | wk_end wrote:
         | Instead of C#, why not F#? You get proper ADTs among many other
         | great features, but you still get all the utility of the .NET
         | ecosystem.
        
           | fluoridation wrote:
           | I get the feeling F# is a second-class citizen in the
           | ecosystem. How likely is it (or is it at all possible) that
           | I'm going to run into some library that doesn't work with F#?
        
             | nightski wrote:
             | None, it's a CLR after all. That said many libraries may
             | not utilize the best features F# has to offer.
        
             | antonyt wrote:
             | It's impossible for a .NET library to not work with F#.
             | It's very possible (and even likely) for a .NET library to
             | prevent you from writing idiomatic F#.
             | 
             | You're right that it's a second-class citizen.
        
               | debugnik wrote:
               | > It's impossible for a .NET library to not work with F#.
               | 
               | This isn't true, C# has been adding new ABI features that
               | didn't interact correctly with F# until the compiler and
               | tooling catched up. For example, the spanification of C#
               | was a huge a pain point and it still is when it comes to
               | tooling.
        
               | antonyt wrote:
               | TIL. Have links to any details about this? I'm very
               | curious!
        
               | pjmlp wrote:
               | Stuff like Roslyn analysers, code generators and
               | interceptors, only take C# semantics into account, the
               | first one also works with VB.NET.
               | 
               | Since its introduction that CLR has the notion of CLS,
               | the Common Language Subset, that any language targeting
               | the CLR should be able to understand.
               | 
               | Anything in the MSIL or metadata, that isn't part of
               | that, requires additional effort from the respective
               | language to be able to expose those features, and many of
               | the more recent improvements, are more in the sense of
               | CLR meaning C# Language Runtime, than Common Language
               | Runtime.
        
               | debugnik wrote:
               | You mostly have to look at the time frames between C#
               | adding features with new ABIs and the time it takes for
               | F# to announce they can actually consume them.
               | 
               | For example, F# used to crash the CLR with
               | InvalidProgramException when setting C# `init`
               | properties. [1] It took almost three years, I think, to
               | release the fix.
               | 
               | [1]: https://github.com/fsharp/fslang-
               | suggestions/issues/904
               | 
               | Another rough example would be spans, and their more
               | general feature, byref-like types (ref struct). These
               | required plenty of compiler support, as they've got
               | special lifetime rules (and more pending to implement
               | `scoped`), they are banned as generic type arguments, and
               | they require ignoring a special Obsolete attribute.
               | 
               | While these were added to F# timely, many language
               | features still break when they interact with them: local
               | functions, computation expressions (even the built-in
               | ones), recursive type inference, and generic intrinsic
               | methods such as `raise`, `defaultof` or `typeof`.
               | 
               | This wouldn't be so bad if C# hadn't "spanified" the
               | shared framework and the entire ecosystem without CLS
               | alternatives for many APIs.
               | 
               | So, these natural F# snippets don't compile:
               | let numbers = [| for span in text.EnumerateLines() ->
               | Int32.Parse span |]            let checkNonEmpty (span:
               | ReadOnlySpan<'T>) =           if span.Length > 0 then
               | span else failwith "Expected non-empty span."
               | 
               | Edit: And I haven't even gotten into the libraries that
               | use reflection expecting you to declare types in ways F#
               | doesn't support.
        
             | Eji1700 wrote:
             | Just to add to the other comments, in my experience a large
             | majority of libraries work fine. Maybe a little tweak here
             | or there, or maybe you need to use some C# esq syntax
             | (which is harder in my case because I so rarely do now).
             | 
             | Generally though, it's pretty easy. Sometimes you'll want
             | to make bindings around various things (not that you need
             | to, but it'll make things smoother).
             | 
             | It's not actually awful, it's just that there's no real
             | documentation of "hey today we're going to take
             | Serilog/whatever and show you how to configure it in F#"
             | style videos, so depending on where your weaknesses lie it
             | can feel frustrating.
        
               | gopherchucks wrote:
               | My experience mirror yours. I really lean into the
               | functional core, imperative shell because of this. Im
               | mostly interacting with C# flavored libraries at the
               | edges and I don't try to force F# paradigms there, I just
               | roll with the punches.
        
           | DaiPlusPlus wrote:
           | The benefits of F# only really apply when using a 100% F#
           | codebase.
           | 
           | Try doing WinForms in F#...
        
             | Eji1700 wrote:
             | I mean that's mostly pretty easy bindings, but yeah it's
             | annoying that you have to do them and feels like you're
             | losing out on the whole damn point of using F#
        
             | Archelaos wrote:
             | That F# has no native GUI framework is the reason why I
             | stick with C#. When coding something with a GUI, the
             | majority of my development time is typically spent in the
             | GUI layer. The rest is some more or less straightforward
             | object relational mapping with some validation an
             | calculations in a business layer where I use Linq to
             | emulate functional programming in C#.
             | 
             | Adding F# to the game would only complicate the scenario,
             | because I would need to connect the F# interfaces somewhere
             | to C# anyway, requiring an additional layer of mappings in
             | many cases.
        
           | lp0_on_fire wrote:
           | Nothing against F# but the developer ecosystem around C# is
           | just plain better, especially if you're already a dotnet
           | shop. Documentation, code examples, tooling, developers who
           | already know the language, etc.
           | 
           | F# doesn't really buy you anything if you're already invested
           | in C# (or even VB.net for those poor souls) unless you have a
           | very specific use-case for it, IMO.
        
           | rafaelmn wrote:
           | F# has it's own warts, tooling isn't as polished, stuff like
           | type providers sound really cool but suck in practice IMO,
           | file ordering being relevant for compilation is bleh...
           | 
           | Every time I try to use it I'm left with a feeling it's not
           | worth the hassle over C#.
           | 
           | C# has been quite nice for 10 years and they keep improving
           | consistently with every version.
        
             | hnthrow289570 wrote:
             | The language itself is nice, the code bases that companies
             | produce with it is not, and sadly that reputation plays
             | into your decision to choose a career stack.
             | 
             | You're going to have patterns from the .NET Framework era
             | being ported to .NET Core projects. It works, but you'll
             | have two paradigms of doing things mixed into your project.
             | 
             | I envy people who only do hobbyist C# so get to work on
             | code bases that have all the newest language feature usage.
        
               | rafaelmn wrote:
               | I definitely dislike most C#/.NET developers/community
               | (every time mediator is mentioned I want to stab myself)
               | and would rather work with people in F#/FP.
               | 
               | But when you have to work on "diverse" development teams
               | having some sort of patterns established (flawed as they
               | are) brings some order to the insanity.
        
               | xeromal wrote:
               | My brain really likes how organizes C# libs tend to be
               | compared to the 50 different organization schemes I deal
               | with in node and python
        
               | bbkane wrote:
               | Does C# impose a lot of organization? I've only worked in
               | one C# codebase but it has partial classes everywhere and
               | TONs of abstraction bloat. I found it difficult to reason
               | about the organization.
        
               | xeromal wrote:
               | I don't think I've really seen a partial class since the
               | webforms/winforms days about 15 years ago. Maybe XAML too
               | but I haven't used XAML in so long.
               | 
               | I think abstraction bloat is one of those things that's a
               | preference. What's bloat to one is organization to
               | someone else. When I hope into a python codebase and it's
               | 900 line file doing computer vision madness, I hope and
               | pray for abstraction bloat. I'm sure there are countless
               | c# bloated codebases but I think that's mostly a function
               | of c# codebases being inside megacorporate or government
               | codebases. The bloat comes from the general inefficiency
               | that those companies run at. I guess the same could be
               | said by the scrappy python startups that put everything
               | in a single file with no types or inversion of control or
               | OOP.
               | 
               | I guess I'm saying that I'd rather deal with the bloat
               | problem than the wild west problem. lol
        
               | mattmanser wrote:
               | C# doesn't really, but VS + the asp.net framework do.
               | 
               | You kinda have to fight against the IDE to not do a lot
               | of things a certain way. For example, it'll automatically
               | namespace new files according to your folder structure.
               | They've also turned a certain amount of automatic linting
               | on to gently suggest/nag you to write in a particular
               | way.Suggest you write classes in certain ways, use newer
               | language features, declare variables in better ways, etc.
               | You can ignore all the nagging, but it's also incredibly
               | easy for the next programmer to walk in and use the quick
               | actions functionality to 'fix' the code in a few clicks.
               | 
               | And the asp.net core team have been incredibly
               | opinionated, forcing a lot of good coding practices on
               | the community.
               | 
               | So on the plus side, they pretty much forced DI to be the
               | way we all work now. It's worked really well. Most
               | library authors have now embraced it with gusto and
               | you'll have a hard time using new libraries in a code
               | base that's DI incompatible.
               | 
               | The bad side is that sometimes they made bad choices, but
               | they are more minor things. Like they bizarrely went all
               | in on JWT tokens which work really badly for corporate
               | apps. Or the IOptions pattern for global settings which
               | sucks compared to normal env variables in any other
               | language. Lots of confusion over how they work on forums
               | and SO.
        
               | eterm wrote:
               | Out of interest, what "patterns from the .NET framework
               | era" do you think don't work well in .Net core?
               | 
               | ( I'm someone who deals all day with legacy .Net
               | framework projects, mixed with the kind of mix of .Net
               | core 3, .Net 6, .net standard 2.0, and .Net 8 projects
               | that you'd expect from a 20+ year old company with 260+
               | projects. And yes, I too envy hobbyists at time. )
        
               | djeastm wrote:
               | >You're going to have patterns from the .NET Framework
               | era being ported to .NET Core projects. It works, but
               | you'll have two paradigms of doing things mixed into your
               | project.
               | 
               | I've been spending the past couple years migrating
               | various platforms from Framework to the new .NET and as
               | long as you've got a head on your shoulders it's not too
               | bad. Also, new projects in .NET are fantastic to work
               | with, imo.
        
               | progmetaldev wrote:
               | I have been doing the same, but I would be willing to bet
               | that you're probably more disciplined than the average
               | .NET developer (or at least have taken the time to learn
               | more than just the surface features available). In my
               | experience, most .NET developers don't take the time to
               | really learn the framework (whether traditional .NET
               | Framework, or the Core framework). It is a great feeling
               | once you've got a Framework project migrated over to
               | current .NET and everything is running along smoothly. My
               | experience has mostly been migrating content management
               | systems.
        
               | pipes wrote:
               | Genuine question, what do you mean by "learn the
               | framework" ? (I mainly work with c#, I constantly worry I
               | am not proactive enough in my learning).
        
               | progmetaldev wrote:
               | I mean the built-in libraries for .NET (whether that is
               | the older .NET Framework, or .NET Core - now labeled just
               | as .NET). One of the biggest benefits to using C# and
               | .NET is the amount of documentation available. If you are
               | still using .NET Framework, and haven't moved to the
               | open-source .NET, I would suggest spending some time
               | learning the open-source version. It's not vastly
               | different, and the good thing is the C# language hasn't
               | changed very much, other than adding new features for
               | developer ergonomics.
        
             | steego wrote:
             | Not to dismiss your criticisms, but I love that file
             | ordering is relevant for compilation for one key reason:
             | 
             | It forces you to reconcile your architecture as it gets
             | bigger and before you accidentally turn it into a
             | monolithic nightmare. IMO, It's more than just a preemptive
             | defense against cyclomatic complexity. Going into
             | unfamiliar F# code, the file ordering is usually a huge
             | clue where to start reading first.
        
           | GiorgioG wrote:
           | Because nobody uses it, Microsoft doesn't invest or promote
           | F# heavily. I'm pissed about all of this, but I accept that
           | eventually C# will get much of F#'s goodness.
        
         | mlinhares wrote:
         | C# is great, likely the best mainstream programming language
         | nowadays, but its in the hands of microsoft and microsoft
         | didn't really care much about building a community or getting
         | it to work natively in other OSes/toolchains.
         | 
         | Its a shame, they even had a second change when Oracle bought
         | sun and no one knew what was going to happen with Java, but
         | fumbled that as well.
        
           | hnrodey wrote:
           | Come again?
        
           | msk-lywenn wrote:
           | This comment feels like it's been written 20 years ago. C#
           | runs natively anywhere and this has been true for at least a
           | decade.
        
             | nullindividual wrote:
             | I'd like a supported port of .NET to the BSDs.
        
           | DaiPlusPlus wrote:
           | > but its in the hands of microsoft and microsoft didn't
           | really care much about building a community or getting it to
           | work natively in other OSes/toolchains.
           | 
           | That statement was true - until 2016. Times change, Microsoft
           | changed.
        
             | pathartl wrote:
             | Yeah, really. I'm distributing a .NET 8 app across Windows,
             | macOS, Linux and x64/arm.
        
               | Salgat wrote:
               | Our developers write C# code using windows/mac/linux
               | (it's up to their own preference) and we deploy to linux
               | containers on AWS. Times have indeed changed.
        
             | mlinhares wrote:
             | Nah, people continue to pick Java or Go when they want to
             | build their enterprise systems if they're not already
             | married to Microsoft.
             | 
             | There's not a single widely distributed infra application
             | in C# out there. When people want to build stuff like
             | kafkas, kubernetes, consuls, they still go to Java/Go/C++.
        
               | lolinder wrote:
               | This comment is kind of out of the blue because no one in
               | this thread is advocating for using C# for infrastructure
               | projects.
               | 
               | Java is frankly a bad choice for that too these days for
               | a new project, and for the same reason: why would you
               | develop infrastructure in a language that requires
               | shipping a runtime when Rust and Go exist? That made a
               | lot more sense back when C/C++ was your only other real
               | choice.
               | 
               | Meanwhile, plenty of people are in this thread telling
               | you that they do in fact use C# to distribute cross-
               | platform applications, but you seem to be uninterested in
               | hearing that.
        
               | giancarlostoro wrote:
               | It's even worse, plenty of Azure itself is C#, and I
               | don't mean a few dashboards, I mean the infrastructure
               | itself, heck the serverless infrastructure's open source
               | and its basically C#. I'm sure the same could be said of
               | AWS and Java (making an assumption), or Google Cloud
               | Platform and Go / Python.
        
               | pjmlp wrote:
               | Ironically, given the past history that lead to .NET
               | existence,
               | 
               | https://devblogs.microsoft.com/java
               | 
               | https://www.microsoft.com/openjdk
               | 
               | https://code.visualstudio.com/docs/languages/java
               | 
               | Because it turns out, making Java running on Azure, on
               | those 60% Linux workloads, is lot of money.
               | 
               | Also plenty of Azure, anything CNCF related, is mostly Go
               | and Rust.
               | 
               | Which is kind of sad, I would expect Azure to be a good
               | contributor for having a .NET presence in the CNCF
               | project landscape.
        
               | giancarlostoro wrote:
               | Fully agreed! It's really interesting how diverse their
               | entire platform really is in the grand scheme of things.
               | One thing I had hoped to see is one of those IL
               | conversion projects that can make JVM bytecode and .NET
               | bytecode co-habitate would have taken off slightly more.
               | I guess the only real way to make those work is to either
               | target both for WASM or implement the standard library of
               | the respective languages in the target platforms, which
               | is a can of worms.
        
               | banashark wrote:
               | Java has been fine for infrastructure projects. There is
               | so much out there that relies on things like
               | elasticsearch, Kafka, or any aws service. Heck even the
               | columnar dbs like pinot and Druid are most likely more
               | used than clickhouse.
               | 
               | C# is also fine for this, just way less popular. Recently
               | ms put out https://github.com/microsoft/Garnet when the
               | redis debacle happened.
               | 
               | The Apache project has helped put out quite a few large
               | infra projects, most of them in Java (I don't know why
               | this is but assume they have lots of resources to help
               | with that, and also that people don't like debugging
               | memory crashes).
               | 
               | Ms has just been not the greatest collaborators with
               | their open source community. A lot of the time it seems
               | like they want to do the initial foundation, then leave
               | "drawing the rest of the owl" to the community, which
               | ends with lots of partially functional things. GUI
               | frameworks, f# tooling, drivers for commonly used
               | critical infra. At the same time, people get upset when
               | ms tries to build a better version of an existing open
               | source project, so there's no winning. They've just put
               | themselves in such a poor spot.
        
               | colonCapitalDee wrote:
               | C# recently came out with Native AOT (ahead of time)
               | compilation that compiles your app to a binary that can
               | run without the .NET runtime (!!), and has fast startup
               | and lower memory overhead. There are a few drawbacks:
               | many reflection and run-time code generation and loading
               | features are unavailable, and LINQ expressions must be
               | interpreted which makes them slow. But it's easy to work
               | around those limitations for a greenfield infra project,
               | and C# has great performance characteristics.
        
               | giancarlostoro wrote:
               | > There's not a single widely distributed infra
               | application in C# out there.
               | 
               | Azure has entered the chat.
               | 
               | Microsoft Orleans has entered the chat.
               | 
               | ASP .NET Core has entered the chat.
               | 
               | Ever played Halo online?
               | 
               | Used StackOverflow? (very likely)
               | 
               | Visited any website on Azure? (most likely)
               | 
               | Microsoft Orleans (Virtual Actor framework) powers the
               | Halo server infrastructure since at least 2011 if not
               | sooner, which arguably the Halo series is insanely
               | popular, especially for online gaming. Also powers plenty
               | of Azure itselfs own infrastructure. I've worked on
               | dozens of scalable C# projects, and know plenty of devs
               | from all over who have as well.
               | 
               | Orleans was so good, EA made their own version in Java
               | which was extensively used there as well, though it looks
               | to be dead, but Orleans is as alive as ever.
               | 
               | Not to mention plenty of Azure itself is open source,
               | like say... Azure Functions, which run inside of ASP .NET
               | Web Services... and are... you guessed it, C#. Plenty of
               | services running on that platform.
               | 
               | https://github.com/Azure/azure-functions-host
        
               | Fervicus wrote:
               | I'll add Bitwarden and Jellyfin to the list of cross
               | platform apps written in C# that are widely popular.
        
               | viraptor wrote:
               | And all the high seas software (sonarr, radarr, etc.)
        
               | benbristow wrote:
               | Bing is well known for being Microsoft's 'dogfood'
               | candidate for new versions of .NET.
               | 
               | https://visualstudiomagazine.com/articles/2018/08/22/bing
               | -ne...
               | 
               | https://www.bing.com/version
        
             | 8338550bff96 wrote:
             | A lot of devs are still living in the 2010s
        
           | kldx wrote:
           | https://isdotnetopen.com/
        
             | metaltyphoon wrote:
             | This always shows up. Comical.
        
           | mattferderer wrote:
           | A very common modern setup is PostgreSQL, C#/.NET, Linux &
           | using JetBrains Rider for the IDE. You don't have to go all
           | in Microsoft to use C#, F# or .NET.
           | 
           | Also .NET has been about "run everywhere on any platform" as
           | their tagline for quite a few years now.
           | 
           | They have had plenty of community fumbles without question. I
           | can't speak to those though. I've seen lots of vocal high up
           | Microsoft employees try to win those fights on the side of
           | the community but no idea what happens internally.
        
             | CharlieDigital wrote:
             | A very common modern setup is PostgreSQL, C#/.NET, Linux &
             | using JetBrains Rider for the IDE
             | 
             | You just described the startup I'm at. All devs are on
             | Arm64 MacBooks and we deploy to Arm64 AWS T4g Linux
             | instances. I'm all VS Code, others are primarily Rider.
             | 
             | .NET is a highly underrated platform for backend; it always
             | puzzles me when teams think about moving from TypeScript to
             | Rust or Go instead of C# because it seems a much smaller
             | leap from TypeScript.
        
               | CBarkleyU wrote:
               | I'm itching to try and deep dive into go as a C# dev. I'm
               | getting sick of enterprise C#, even if it is .NET8
               | 
               | I guess the grass is always greener somewhere
        
               | HideousKojima wrote:
               | Worst part for me about Go is how exceptions are usually
               | basically handled the way that they would be with a
               | Result<T> in Rust, but without an actual Result<T> type.
               | That and how nulls are handled, feels like the worst of
               | both worlds.
        
               | CharlieDigital wrote:
               | Maybe startup C# feels very different; it feels much more
               | like TypeScript and I more or less write C# like I would
               | write TypeScript.
        
         | giancarlostoro wrote:
         | For me its C# and Python, though I like Rust, D and lately been
         | getting into Nim. I want to like Lisp, but its just not
         | something I see someone paying me to work in.
        
           | radicalbyte wrote:
           | C# for real work, Python for scripting or specific use-cases
           | where it has excellent library support & Golang if I need
           | something closer to C or very lightweight or easy for people
           | to understand.
           | 
           | Rust is interesting but the compile times aren't great.
        
             | giancarlostoro wrote:
             | I use Python for real work too, but my current employer
             | does not use it as much.
        
         | paulddraper wrote:
         | > It's incredibly hard to go back to a language without even
         | basic ADTs after seeing the light.
         | 
         | How did we ever prioritize implementation inheritance over
         | ADTs?
         | 
         | I don't know, but it was a mistake.
        
           | JoshTriplett wrote:
           | How widely understood and appreciated were algebraic data
           | types at the time Cfront was first floated as the precursor
           | to C++? To what degree had they shown up in mainstream
           | languages?
        
             | tialaramex wrote:
             | Well if you want to compare to why C++ has multiple
             | inheritance, you need to first explain which mainstream
             | languages in the mid-1980s had multiple inheritance so we
             | can contrast.
             | 
             | Like algebraic data types, multiple inheritance wasn't
             | novel, but what were the big famous languages from that
             | time which showed this was a must have for Bjarne's
             | language?
        
               | JoshTriplett wrote:
               | That's not the comparison I'm making. It's possible for a
               | proposed language to add something despite not being
               | popular at the time, but that's not the _default_. I 'm
               | suggesting that if ADTs were not already popular at the
               | time, it would be the unsurprising default for them to
               | not be in a new language. In _today 's_ language
               | landscape, by contrast, it would be surprising and
               | disappointing for a new language to _not_ have either
               | ADTs or a specific alternative they propose for solving
               | the same problem.
        
               | tialaramex wrote:
               | But the actual answer is revealing. Bjarne implemented
               | multiple inheritance in C++ because it was _easy_. It 's
               | a New Jersey language. The priority is simplicity of
               | implementation.
               | 
               | It's not about what was popular, or that Stroustrup was
               | at Cambridge not Edinburgh (all the exciting Programming
               | Language theory stuff in that era like ML was from
               | Edinburgh as you perhaps know). If ADTs were easy, C++
               | would have ADTs.
        
           | bazoom42 wrote:
           | Probably bescuse OOP was a great fit for desktop GUI's. This
           | drove OOP into the mainstream. ML-style languages never had a
           | similar killer application.
        
           | munificent wrote:
           | I think a similar but perhaps more accurate question is how
           | did we prioritize virtual dispatch over ADTs?
           | 
           | And I think the answer has a lot to do with the expression
           | problem [1] and which kinds of extensibility were needed in
           | the kinds of programs that languages at the time were
           | designed for.
           | 
           | OOP with subtyping and virtual dispatch makes it very easy to
           | define an interface with a set of methods, and then have an
           | open-ended set of concrete classes that all reliably
           | implement those methods. It makes it easy to say "I don't
           | know what data types I'll need yet, but I do know what
           | operations I'll need."
           | 
           | ADTs flip that around. That make it easy to express "I don't
           | know what operations I'll need yet, but I do know what data
           | types I'll need."
           | 
           | For the kinds of simulations that Kristen Nygaard and Bjarne
           | Stroustrup were writing, and then later the large GUI
           | applications that C++ users were building, it seems that the
           | former was more useful than the latter.
           | 
           | [1]: https://journal.stuffwithstuff.com/2010/10/01/solving-
           | the-ex...
        
         | CharlieDigital wrote:
         | It's possible to add DUs in C# today with some third party
         | packages.
         | 
         | - https://github.com/domn1995/dunet
         | 
         | - https://github.com/mcintyre321/OneOf
         | 
         | Quite good and ergonomic with the source generators removing a
         | lot of the boilerplate.
         | 
         | I have a practical example here using OneOf with .NET Channels:
         | https://chrlschn.dev/blog/2024/07/csharp-discriminated-union...
        
           | jeremycarter wrote:
           | Actually not a bad solution. Thanks for sharing
        
         | pseudopersonal wrote:
         | You must really hate REPLs. You cling to your ADTs, I cling to
         | my REPLs.
        
           | indrora wrote:
           | I can't speak to Rust but there have been plenty of C# repls
           | in the past.
           | 
           | https://fuqua.io/CSharpRepl/ For one
        
           | maxbond wrote:
           | I've seen comments like this a few times, and I think there's
           | a misunderstanding at play. I think people who are used to
           | using REPLs look at people who aren't and conclude that,
           | since they don't have a REPL in their toolbox, they don't
           | have a tool in there that does what a REPL does. But I think
           | most of the time, they are accomplishing the same tasks with
           | a different workflow.
           | 
           | For instance, I use throwaway files instead of REPLs, even
           | when I have access to one. I'm still running small and
           | isolated experiments to build my understanding, but I get to
           | use the editor I'm comfortable with and it's easy to copy the
           | code into my project afterwards.
           | 
           | The other thing I used to use REPLs for was
           | introspection/looking up documentation, which my LSP handles
           | nicely. You can think of the LSP as a sort of REPL integrated
           | seamlessly into the editor; when I have a question, I'll
           | query the LSP by writing a line of dummy code that causes the
           | LSP to generate an annotation with the answer. So for
           | instance if I want to know if `foo` implements a certain
           | trait/interface, I can write a variable declaration like `let
           | x: Box<dyn MyTrait> = Box::new(foo)` and see if my LSP
           | annotated that as a type error. I can do this very quickly
           | and without leaving my editor, so it helps me stay in the
           | flow of things.
        
         | tester756 wrote:
         | I've been writing C, C++, C# and some js/sql/ts/python for
         | money. Lua for lulz.
         | 
         | And *nothing* gets even fucking close in terms of productivity
         | to C#.
         | 
         | Great language, mature and robust ecosystem with sane
         | compilation times and
         | 
         | great tooling: package manager, test runner, strong debugger,
         | one CLI with almost all tools needed.
         | 
         | I wish C++ was half as enjoyable as C# is.
        
           | TillE wrote:
           | I'm a big fan of C#, but to be fair nothing else you've named
           | is really aiming for the same niche. The better comparison
           | would be Swift, Go, Java/Kotlin, stuff like that.
           | 
           | But yeah I think C# is often unfairly viewed as a boring
           | enterprise-y language when it's actually become a very comfy
           | swiss army knife.
        
             | LtWorf wrote:
             | If it wasn't so linux distribution unfriendly...
        
               | nickcox wrote:
               | In what ways?
        
           | darby_nine wrote:
           | > And _nothing_ gets even fucking close in terms of
           | productivity to C#.
           | 
           | C# gets compared to java a lot for a good reason, and yet the
           | languages and ecosystems have since diverged in their
           | priorities and abilities. This gets even more complicated
           | when considering F# and Scala. Do you think Java would offer
           | similar productivity, or are there aspects to the C# that are
           | --in your personal experience--uniquely productive?
        
           | Salgat wrote:
           | The standard library alone is insane. The only time I need an
           | outside library is for something specific like a database or
           | sdk.
        
         | foota wrote:
         | I also really like rust's support for deriving traits. Being
         | able to generate hashes, equality, debug strings, etc., is
         | really convenient for hacking around.
        
       | orra wrote:
       | Is the terminology slightly off? AIUI TypeScript has type unions.
       | 
       | But this looks like a discriminated union which I'd recognise
       | from F# or Haskell. The distinction I'd draw is that for a DU
       | there are named case constructors. Yes?
        
         | wk_end wrote:
         | TypeScript has "union types". This proposal refers to them as
         | "ad hoc unions". I don't think "type union" is a term of the
         | art - at least I haven't heard it before. It seems to be
         | something the C# people are making up to describe sum types.
        
           | SideburnsOfDoom wrote:
           | > I don't think "type union" is a term of the art
           | 
           | "sum type" is the term of art in Computer Science theory, but
           | "union type" is also used.
           | 
           | See
           | 
           | https://en.wikipedia.org/wiki/Type_theory#Sum_type
           | 
           | https://en.wikipedia.org/wiki/Tagged_union
        
       | LandR wrote:
       | Ha! Under covariance / contra variance
       | 
       | > Note: Have Mads write this part.
       | 
       | :)
        
         | lyu07282 wrote:
         | Mads Torgersen, lead designer of C#, just to clarify lol
        
       | wackro wrote:
       | What would the rough timeframe be for seeing adoption of this
       | into the language?
       | 
       | I was considering introducing the OneOf library into our
       | codebase, but if this is < a year or so away it might not be
       | worth the effort.
        
         | leosanchez wrote:
         | 3 years away at least.
        
         | SideburnsOfDoom wrote:
         | The linked says "Proposed, Prototype: Not Started,
         | Implementation: Not Started, Specification: Not Started"
         | 
         | .NET releases are every November, and I would be very surprised
         | to see this in November 2024. More likely November 2025 at
         | soonest. But check back to that page later.
        
           | ygra wrote:
           | Definitely no way for Nov 2024 since they're already no
           | longer merging features for that release. Considering the
           | scope of this, I'm doubtful for next year as well. .NET takes
           | their time with features to get them right as well. They
           | don't seem to take as long as Java, but maybe most new
           | features are also not as widely publicized.
        
         | louthy wrote:
         | You won't be able to leverage C#'s pattern-matching effectively
         | with a library.
         | 
         | Really though, you don't need a library to do sum-types in C#:
         | 
         | It's best to just use records for now:                   public
         | abstract record Maybe<A>         {             private Maybe()
         | { }                  public sealed record Just(A Value) :
         | Maybe<A>;             public sealed record Nothing : Maybe<A>;
         | }
         | 
         | The private constructor and sealed case-types stops anybody
         | else deriving from `Maybe<A>`, so the type is effectively
         | closed and finite.
         | 
         | You can then construct like so:                   var mx = new
         | Maybe<int>.Just(123);         var my = new
         | Maybe<int>.Nothing();
         | 
         | Then you can use the pattern-matching:                  var r =
         | mx switch        {           Maybe<int>.Just (var x) => x,
         | Maybe<int>.Nothing      => 0        };
         | 
         | Of course, we don't get exhaustiveness checking, that'll have
         | to wait for the proper sum-types (although the compiler does
         | some pretty good work on this right now); but until then this
         | is the most expressive and powerful way of doing sum-types in
         | C#.
         | 
         | And, if you want a load of pre-built ones, then my library
         | language-ext will help [1]
         | 
         | [1] https://github.com/louthy/language-ext/
        
           | S04dKHzrKT wrote:
           | Not quite closed. The compiler generated protected
           | constructor can still be used.                   public
           | record UhOh<A> : Maybe<A>         {             public UhOh()
           | : base(new Maybe<A>.Nothing())             {             }
           | }              Maybe<int> m = new UhOh<int>();
        
         | owisd wrote:
         | It's a different kind of ugly, but you can use the Visitor
         | pattern to get the same exhaustiveness checking at compile time
         | without a library.
        
         | CrimsonCape wrote:
         | It's not a bad idea to evaluate OneOf. This proposal relies on
         | records, so every use instance requires heap allocations and
         | dereferencing which has memory and time implications
         | respectively.
         | 
         | I implemented my own option types entirely differently via ref
         | structs where the "non-option" becomes either a default of a
         | value type or a null reference (size of a pointer). Then (in
         | theory) the overhead is reduced to a single dereference with
         | everything else being stack-allocated.
         | 
         | I then built the same thing as a non ref (regular) struct
         | version for storing in a field. So I have Options.Quick and
         | Options.Long namespaces with practically the same struct design
         | just the former is a ref struct.
         | 
         | This has served me very well, is very fast. It just doesn't
         | have the exhaustive checking and risks derefing a null
         | reference, but in practice is not a big issue.
         | if(option.IsT1){ doSomething(option.ValueT1)};
         | if(option.IsT1){doSomething(option.ValueT2)}; // this would
         | throw an exception, i.e. a bug, but its very readable to catch
         | the bug
        
       | ctenb wrote:
       | I find it strange and slightly confusing that tey call it "union
       | types". The correct term would be "sum types" or "discriminated
       | union types", union types being the union of two types with no
       | distinctive tag between the two cases. E.g. |Int [?] Int| [?]
       | |Int|, while |Int + Int| [?] 2 |Int|
        
         | lolinder wrote:
         | > I find it strange and slightly confusing that tey call it
         | "union types". The correct term would be "sum types" or
         | "discriminated union types"...
         | 
         | You mean like this?
         | 
         | > A proposal for type unions (aka discriminated unions) in C#.
         | 
         | Discriminated unions are a type of union type, hence the name,
         | and in terms of how they're used in everyday development they
         | fill a very similar role. If they have no intention of
         | supporting pure TypeScript-style unions (which I'm sure they
         | don't) then I don't know what the harm is of shortening
         | "discriminated union" to "union" in the context of C#.
        
           | ctenb wrote:
           | I've read the proposal, and I'm just pointing out that "aka
           | discriminated unions" is incorrect or at the very least
           | confusing. Call it "sum types" if you don't like the term
           | "discriminated union types", it's both correcter and shorter
           | than "union types"
        
         | noelwelsh wrote:
         | AFAICT from a quick skim of the proposal is actually includes
         | both sum types (called union types) and union types (called ad
         | hoc unions) under the same proposal. This is, to me, very
         | confusing. They work in very different ways.
        
           | jsmith45 wrote:
           | At runtime every value of any of these types are tagged in
           | some some way. The struct based ones with an explicit tag
           | member that is not visible at the language level. All the
           | rest by way of the object's runtime type (v-pointer).
           | 
           | Which means the fact that some look at a language level like
           | a traditional closed sum type, and others look more like a
           | union type is pretty much just that, looks. Even the "ad hoc
           | unions" are basically functioning as sum types here, just an
           | ad hoc sum type whose type constructors are other types, and
           | abusing the fact that classes or boxed structs all have a
           | vptr that can be used as the discriminator.
           | 
           | This all compiles down to code that pattern matches on either
           | the explicit tag member, or on the runtime type, and after
           | the pattern match you basically have a normal type to work
           | with. Hence why they are lumping it all together.
        
         | munificent wrote:
         | The proposal does both sum types and union types.
        
       | ctenb wrote:
       | I'm currently using nested records with a private constructor in
       | combination with the nuget package
       | https://github.com/shuebner/ClosedTypeHierarchyDiagnosticSup...
       | to make sure the switch types do not require a `_` case. This is
       | essentially the desugared version of their "Union Classes"
       | proposal. This already works very well. Still, I like this
       | proposal because it would be nice if the nuget package would
       | become unnecessary and the syntactic sugar is also nice to have.
        
         | S04dKHzrKT wrote:
         | Do note that record types aren't closed even with a private
         | constructor. The compiler will generate a protected constructor
         | for copy operations which can then be inherited from. To
         | prevent this, you have to define your own protected constructor
         | and have it throw a runtime exception if it isn't a valid case.
        
           | ctenb wrote:
           | You are technically correct (the best kind of correct), but
           | in practice I've not found this to be a problem, certainly
           | not something that is ofsetting the vast usefulness of this
           | data pattern.
        
             | S04dKHzrKT wrote:
             | I've found the same to be true as well.
        
       | Alifatisk wrote:
       | Even dotnet is looking at implementing type unions, why can't
       | Dart finally agree on adding this aswell!?
        
         | munificent wrote:
         | We added sum types and exhaustive pattern matching in Dart 3.0.
         | 
         | Union types (what the proposal here calls "ad hoc unions") are
         | a separate, much harder feature whose value proposition is less
         | clear. The cost of adding a new kind to the type system is
         | quite large because it impacts method resolution, overriding,
         | type inference, subtyping, least upper bound, generics, type
         | promotion, etc.
         | 
         | It can be worth it (for example, we added tuple/record types in
         | Dart 3.0), but the value proposition must be correspondingly
         | high to justify it. It's not clear that union types meet that
         | bar yet. In many if not most of the places where users are
         | asking for it, it often seems like what they really want is
         | overloading or some other feature.
        
       | tubthumper8 wrote:
       | Can anyone explain why this is called "type unions"? I've never
       | heard it named that before. It's a bit weird, because it's not a
       | union across types (like ALGOL68), it appears to be a tagged
       | union like in ML-family languages.
       | 
       | Is this just a case of "C# developers like to make up different
       | names than established terminology"? (see: SelectMany,
       | IEnumerable, etc.)
        
         | kgeist wrote:
         | "Tagged" sounds like an implementation detail to me (that it
         | has a "tag" internally to tell between types). I suspect they
         | added "type" to "union" to make it clear what is about (about
         | types). The syntax itself has just "union", judging by the
         | document.
         | 
         | UPD. They have this in the FAQ:                 Q: Why are
         | there no tagged unions?       A: Union structs are both tagged
         | unions and type unions. Under the hood, a union struct is a
         | tagged union, even exposing an enum property that is the tag to
         | enable faster compiler generated code, but in the language it
         | is presented as a type union to allow you to interact with it
         | in familiar ways, like type tests, casts and pattern matching.
        
       | LAC-Tech wrote:
       | I always think it's a shame that instead of C# becoming a better
       | OO language, they keep trying to become an uglier F#. IE, why is
       | the syntax for multiple dispatch still so clunky?
       | 
       | I know I know, pseudo OO took over the world, and then people
       | revolted against it, so the easiest thing to do to stay relevant
       | is to become "slightly functional with curly braces", rather than
       | to actually try and do OO well.
        
         | mrkeen wrote:
         | I have the same objection but coming from the other side. The
         | mainstream languages used to have pervasive mutation and side-
         | effects. Now they have pervasive mutation and side-effects with
         | lambdas.
         | 
         | But my question is: what is missing from the OO side? What
         | would it take for C# and/or others to "do OO well"?
        
           | LAC-Tech wrote:
           | To be clear I don't really come from "the other side". I very
           | much enjoy and like functional programming. But again, that's
           | what F# is for, right? If we push the state of the art in
           | another directrion, the whole landscape benefits more -
           | because I think we both agree C# is never going to be a
           | better FP language than F#.
           | 
           | As for what C# could do better, the OO solution to the
           | problem pattern matching solves is multiple dispatch - with
           | single dispatch languages you need things like the visitor
           | pattern. Making that a first class part of C# would be
           | fantastic.
           | 
           | https://shawnhargreaves.com/blog/visitor-and-multiple-
           | dispat...
        
           | lyu07282 wrote:
           | > What would it take for C# and/or others to "do OO well"?
           | 
           | Multiple inheritance. (j/k)
        
             | LAC-Tech wrote:
             | Unironically this.
             | 
             | Having "interfaces" and "abstract classes" is just a
             | kludge, multiple inheritance covers all of this with one
             | language construct.
             | 
             | The "diamond problem" is a C++ issue, every other language
             | solved it.
        
         | tester756 wrote:
         | What does "better OO" mean here?
         | 
         | Which concepts or feature you have on mind?
        
       | pipeline_peak wrote:
       | It's ironic when things like this get added back into C-styled
       | languages that were intended to be easier than their
       | predecessors.
       | 
       | Seems more confusing than useful. Wish they'd stop adding onto
       | this ever expanding language.
       | 
       | It's like these people get bored of plain old functions and
       | objects, read an academic paper and go "i think I'll add that
       | next".
        
       | riizade wrote:
       | I don't have anything insightful to add, I just want to add my
       | voice to the choir of people saying that after using sum types,
       | working in a language without them feels unnecessarily
       | restrictive and awkward.
       | 
       | If this feature were implemented, it would take C# to a top
       | contender for a daily driver language for me, and would make me
       | feel a lot more confident in choosing it for projects where the
       | C# ecosystem is already present, such Godot's C# version.
       | 
       | I'm really excited about this proposal, and I don't know anything
       | about C#'s development pace, but I'll be watching from the
       | sidelines hoping I can use union types in production in 2025 or
       | so.
        
       ___________________________________________________________________
       (page generated 2024-08-07 23:00 UTC)