5 ms·
These C# issues arguably demonstrate haphazard additions that didn't align with good taste: * Too many overlapping concepts for referring to code by value and
by ljackman 7y ago
These C# issues arguably demonstrate haphazard additions that didn't align with good taste:
* Too many overlapping concepts for referring to code by value and defining them inline: events, delegates, anonymous delegates, and lambdas.
* Lambdas that generate magic types rather than slotting into SAM types. This works great for functional languages, sure, but doesn't fit well into a class-based OOP language.
* `ref` and `out` parameters to appease archaic COM APIs.
* Tuple unpacking, which makes sense independently but then bizzarely tries to integrate with `out` parameters.
* A nice "ex nihlo" object literal syntax that shoots itself in the foot by undermining immutability due to requiring settable fields. (TBF, later versions fixed this IIRC.)
* Inline functions in methods in a language that already has lambdas and methods; it's just bloat.
* `as` casting that yields nulls. Did it really need a whole new syntax just to handle null more concisely specifically for casting?
* `partial` classes. Encouraging even more code generation with features like this is a questionable idea and wasn't necessary in other languages.
* `dynamic`. Even if there are use cases for it, it's strange in an otherwise static language with a top-level Object type anyway. I'm saying this as someone who's perfectly happy with fully dynamic languages like Erlang and Lisp. I just don't see the point of adding it to C# specifically.
* C-style enumerations.
* Properties. Invoking side-effects on something that is syntactically indistinguishable from an attribute read is a bad idea. Auto-generation of Java's verbose getters would be long overdue, but the caller site shouldn't be the same a la C#.
* Extension methods. It seems quite ad hoc compared to static addition of types to common operations in other languages, like imported traits in Rust or typeclasses in Haskell.
* Proposed "shapes". Looks like a good idea by itself but will overlap too much with default method implementation and other existing mechanisms.
* Interfaces prefixed with `I`. Not strictly a language problem but an ecosystem one. I shouldn't need to know what _sort_ of type I'm dealing with, that's '90s Hungarian notation.
* Nullable reference types. Getting rid of null is good, but this proposal became confusing. They mentioned opting in assembly-wide for a while but there was then a conversation about having it just warn in some cases. I need to read the latest literature around this, but it seemed less elegant than Java just adding a monad-like Optional type and not adding loads of special-case operations with question marks everywhere.
Despite this, I still admire a lot of the design decisions behind C#. LINQ was great. `async/await`, despite my belief of its inferiority to Goroutines/Project Loom/Erlang processes, was still a great innovation from Midiori at the time. Value types were obviously right to be implemented early on. Assemblies were a good idea. Private by default rather than package-accessibility was a nice touch, as was the `override` keyword. The C# team are smart people who know what they are doing!
As an aside, I used to be firmly against erased generics, but reading more about the tradeoffs from the likes of Gilad Bracha has caused me to reconsider.
- phillipcarter 7y ago> Nullable reference types. Getting rid of null is good, but this proposal became confusing. They mentioned opting in assembly-wide for a while but there was then a conversation about having it just warn in some cases. I need to read the latest literature around this, but it seemed less elegant than Java just adding a monad-like Optional type and not adding loads of special-case operations with question marks everywhere. Nullable Reference Types (NRTs) is released, so it's important to talk about what exists in an LTS form today rather than something from a draft proposal. Firstly, there's the surface-level stuff. Reference types can be explicitly be marked as `foo?` to indicate to the compiler that the type is nullable. Mismatches are warnings to ensure backwards compatibility, since billions of lines of perfectly valid code today can't just start emitting errors across an entire codebase. But the far more interesting side of NRT isn't that, but the compiler analysis that goes into it. It's an incredibly advanced and thorough flow-based typing system that catches numerous complicated scenarios, and a system that can be (and is) improved over time without incurring a risk of a breaking change. This analysis is equally applied to the existing nullable value types, so it's a unified model. The other interesting side of NRT is that isn't a one-and-done feature. There's a long rollout period where the .NET ecosystem adopts this way of dealing with reference types, and to do so there need to be tools for component authors and application developers to adopt it incrementally and at their own pace. Everything in the design is incredibly deliberate and well thought-out, with numerous past designs (such as a "sidecar" format for annotations and a mechanism for managing updates to that in parallel with a package or framework!). It is imperfect, but perhaps the best that can be done given the constraints a 20 year old language imposes. That said, I really the world could be different. Since I prefer (and work on) a typed functional language where `null` isn't much of a problem due to a different core language design, the incredible amount of engineering effort that went into NRT for C# feels slightly strange to me. But my only reasonable alternative to not making progress on this problem is, "just use a different language", which most developers do not find reasonable. Additionally, the pedantic side of me doesn't feel that Java's optional is an any way reminiscent of monadic programming. Java simply lacks numerous features to enable this style of programming in a way that the majority of Java developers would utilize.
- ljackman 7y ago