Hacker News new | ask | show | jobs
by phillipcarter 2380 days 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.

1 comments

Thanks for the clarification on the final NRT behaviour. Just to say, my point about "didn't align with good taste" was itself lacking taste. I'm sure each of those features I critiqued made sense as they were proposed at the time and were just considering different use cases and tradeoffs.

Did it drop the idea of assembly-wide opt-ins to stricter behaviour, meaning all NRTs can be reasoned about in the same way without considering a configuration flag somewhere like PHP? That does sound like an improvement.

Doing the change gradually, without breaking existing code or requiring potentially-ecosystem-breaking opt-ins does seem eminantly sensible and user-friendly. I was being too harsh to C# here. Java's `Optional` doesn't even warn about it itself being null, for example. You need static tooling and code analysis for that. C#'s solution does at least try to solve that, albeit at the cost of more complexity.

I said "monad-like" rather than "monadic" for that reason, but arguably it doesn't even go far enough to be considered monadic-like. Certainly this article would agree: https://blog.developer.atlassian.com/optional-broken/

> Did it drop the idea of assembly-wide opt-ins to stricter behaviour, meaning all NRTs can be reasoned about in the same way without considering a configuration flag somewhere like PHP? That does sound like an improvement.

There's three levels of opt-in/out:

* Source directives, for opting in/out a single scope (typically a whole file, but it can in be as small as a single method)

* Project file directive via MSBuild property, for opting in/out a single project/assembly

* The MSBuild property can be set in a Directory.build.props file, opting in/out all projects in a directory and its children

In the long term, this property will likely be implicitly set to true for new projects (and perhaps all projects in the very long term), but for now everything is opt-in.

Personally, I think that one of the biggest areas for tooling improvement is to better recognize when you're in a nullable-enabled context. It's pretty obvious when you look at source code, but when you've got a million LoC codebase and only a subset uses it, making developers aware of that is certainly a challenge.

The other interesting thing to consider here is that even if the C# compiler freezes all future improvements to NRT analysis, there is a level of breaking changes that will occur over time as packages and frameworks adopt the feature and rev their versions. This transformation won't be without pain for existing codebases, and some may never adopt the feature (especially if they're the kind of codebase that doesn't really modernize). Interesting times ahead.