Whether the value is null (and under which conditions) is encoded into the nullability of return value. Unless you work with a project which went out of its way to disable NRTs (which I've sadly seen happen).
.NET Native is essentially dead. NGEN is specific to .NET Framework. It is possible to dynamically link multiple binaries with the same runtime with NativeAOT but it is very exotic and undocumented path that is not guaranteed to be stable. Not much worse than Go's dynamic linking which also requires the same compiler version.
In theory, in practice the almost resourless WinUI/WinAppSDK team is yet to change the minds of the few companies that believed in UWP to move away from it, due to the amount of missing features after 5 years of Project Reunion.
In fact, the ones that care to port their applications might create Web versions than bother with Native AOT.
in .NET, async/await does not protect you from data races and you are exposed to them as much as you are in Go, but there is a critical difference in that data races in .NET can never result (not counting unsafe) in memory safety violations. They can and will in Go.
AKA people who are willing to be honest about its faults. .NET has a lot of social commentary on how stable and robust it is yet for some reason in every project I've had the displeasure of brushing up against a .NET solution it's always "we're updating" and the update process magically takes longer than building the actual feature.
Or what is a pretty standard feature in other tech-stacks needs some bespoke solution that takes 3 dev cycles to implement... and of course there's going to be bugs.
And it's ALWAYS been this way. For some reason .NET has acolytes who have _always_ viewed .NET has the pinnacle of programming frameworks. .NET, Core, .NET framework, it doesn't matter.
You always get the same comments. For decades at the point.
Except the experience and outcomes don't match the claims.
Just before I get the reply, I'm pretty familiar with .NET since the 2000's.
Being forced to compromise your domain model. Yes the product has improved this greatly in recent years but it’s still inadequate IMO.
Fluent syntax can at first seem like the product has achieved persistence ignorance nirvana but then you have to compromise a little here, compromise a little there, until some point, if you’re still thinking critically about the design, you realize that you’re writing your app in Entity Framework as much as you are writing it in C#, as I mentioned.
Passing around a large mutable blob (dbcontext) which, if not managed with the utmost discipline by your dev team, can make it necessary to understand what large swaths of the code do before you can adequately understand what any small part of the code does.
It is as simple as what you get with Cargo, and possibly even more readable.
.NET, unlike Go, has all needed management commands built into its CLI too: dotnet new {template}, dotnet add/remove package, dotnet sln add/remove, etc.
> has an edge on some domains due to having unboxed types
If a language makes "unboxed types" a feature, a specific distinction, and has to sell "removing global lock" as something that is a massive breakthrough and not table stakes from 1.0, it can't possibly be compared to F# in favourable light.
I vouched for your dead comment because I get where you're coming from. But OCaml 1.0 was in 1996 in an academic setting. I care about the language I can use now, not the one I didn't use.
Let's not dismiss that their solution to remove the global lock has been both simpler than Python's (better funded?) ongoing work and backwards compatible; and the multicore support came with both a memory model with strong guarantees around data races, and effect handlers which generalize lightweight threads.
I agree that lack of unboxed types is a fatal flaw for several domains (Worse, domains I care about! I usually consider Rust/Go/.NET for those.) But when the comparison with OCaml is favourable to F#, the gap isn't much wider than just unboxed types. F# has an identity crisis between the ML parts and the CLR parts, its C# interop is decaying as the ecosystem evolves, and newer .NET APIs aren't even compatible with F# idioms (here's a .NET 6+ example [1]).
Ref structs are hard to solve because they require, in some ways, a lot more work than the way C# solved the lifetimes as the lifetimes in F# may need to become much closer to what they are in Rust. Some constructs may not be able to consume them still (can’t move ref struct into an object with effective lifetime ‘static) and there really is no easy solution short of language editions and breaking standard library changes. A limited improvement is possible, but we’re still in the realm where the lifetimes may become a part of type identity or somehow generalized over, which is hard.
OCaml predates multicore CPUs. Having a global lock was basically free at the time it was invented. It's totally crazy to dislike a language because the authors made a decision that was obviously correct at the time.
F# uses the CLR term "value types" instead, or sometimes "struct" types as in the C# keyword.
These are usually defined with the [<Struct>] attribute over a regular type definition, or using the struct keyword before tuple/anonymous record types. Also, the original 'T option type now has a value-type successor 'T voption.
> C#/f# are safer from that particular problem and more performant than either go or swift, but have suffered from the same deserialization attacks that java does.
If anything, the Mono runtime is what prevents heavier (ab)use of SIMD types in the standard library, or at least it causes additional required effort to make it not regress in performance since it's nowhere near as capable as CoreCLR.
As C# SIMD had a such a long head start in Mono (your linked MS blog post has their timeling starting only in 2014) it should probably get a bit more credit.
It was after all enabling games to use SIMD with C# (with MonoGame and Unity), even if their interface wasn't the winning one in the end.
reply