The .NET Stacks #18: RC1 is here, the fate of .NET Standard, and F# with Isaac Abraham

This week, RC1 is here, we talk about .NET Standard, and discuss F# with Isaac Abraham.

Dave Brock
Dave Brock

.NET 5 RC1 is here

This week, Microsoft pushed out the RC1 release for .NET 5, which is scheduled to officially “go live” in early November. RC1 comes with a “go live” license, which means you get production support for it. With that, RC1 versions were released for ASP.NET Core and EF Core as well.

I’ve dug deep on a variety of new features in the last few months or so—I won’t  rehash them here. However, the links are worth checking out. For example, Richard Lander goes in-depth on C# 9 records and System.Text.Json.

The fate of .NET Standard

While there are many great updates to the upcoming .NET 5 release, a big selling point is at a higher level: the promise of a unified SDK experience for all of .NET. The idea is that you’ll be able to use one platform regardless of your needs—whether it’s Windows, Linux, macOS, Android, WebAssembly, and more. (Because of internal resourcing constraints, Xamarin will join the party in 2021, with .NET 6.)

Microsoft has definitely struggled in communicating a clear direction for .NET the last several years, so when you pair a unified experience with predictable releases and roadmaps, it’s music to our ears.

You’ve probably wondered: what does this mean for .NET Standard? The unified experience is great, but what about when you have .NET Framework apps to support? (If you’re new to .NET Standard, it’s more-or-less a specification where you can target a version of Standard, and all .NET implementations that target it are guaranteed to support all its .NET APIs.)

Immo Landwerth shed some light on the subject this week. .NET Standard is being thrown to the .NET purgatory with .NET Framework: it’ll still technically be around, and .NET 5 will support it—but the current version, 2.1, will be its last.

As a result, we have some new target framework names: net5.0, for apps that run anywhere, combines and replaces netcoreapp and netstandard. There’s also net5.0-windows (with Android and iOS flavors to come) for Windows-specific use cases, like UWP.

OK, so .NET Standard is still around but we have new target framework names. What should you do? With .NET Standard 2.0 being the last version to support .NET Framework, use netstandard2.0 for code sharing between .NET Framework and other platforms. You can use netstandard2.1 to share between Mono, Xamarin, and .NET Core 3.x, and then net5.0 for anything else (and especially when you want to use .NET 5 improvements and new language features). You’ll definitely want to check out the post for all the details.

What a mess: .NET Standard promised API uniformity and now we’re even having to choose between that and a new way of doing things. The post lays out why .NET Standard is problematic, and it makes sense. But when you’re trying to innovate at a feverish pace but still support customers on .NET Framework, the cost is complexity—and the irony is that with uniformity with .NET 5, that won’t apply when you have legacy apps to support.

Dev Discussions: Isaac Abraham

As much as we all love C#, there’s something that needs reminding from time to time: C# is not .NET. It is a large and important part of .NET, for sure, but .NET also supports two other languages: Visual Basic and F#. As for F#, it’s been gaining quite a bit of popularity over the last several years, and for good reason: it’s approachable, concise, and allows you to embrace a functional-first language while leveraging the power of the .NET ecosystem.

I caught up with Isaac Abraham to learn more about F#. After spending a decade as a C# developer, Isaac embraced the power of F# and founded Compositional IT, a functional-first consultancy. He’s also the author of Get Programming with F#: A Guide for .NET Developers.

Isaac Abraham

I know it’s more nuanced than this: but if you could sell F# to C# developers in a sentence or two, how would you do it?

F# really does bring the fun back into software development. You’ll feel more productive, more confident and more empowered to deliver high-quality software for your customers.

Functional programming is getting a lot of attention in the C# world, as the language is adopting much of its concepts (especially with C# 9). It’s a weird balance: trying to have functional concepts in an OO language. How do you feel the balance is going?

I have mixed opinions on this. On the one hand, for the C# dev it’s great—they have a more powerful toolkit at their disposal. But I would hate to be a new developer starting in C# for the first time. There are so many ways to do things now, and the feature (and custom operator!) count is going through the roof. More than that, I worry that we’ll end up with a kind of bifurcated C# ecosystem—those that adopt the new features and those that won’t, and worse still: the risk of losing the identity of what C# really is.

I’m interested to see how it works out. Introducing things like records into C# is going to lead to some new and different design patterns being used that will have to naturally evolve over time.

I won’t ask if C# will replace F#—you’ve eloquently written about why the answer is no. I will ask you this, though: is there a dividing line of when you should use C# (OO with functional concepts) or straight to F#?

I’m not really sure the idea of “OO with functional concepts” really gels, to be honest. Some of the core ideas of FP—immutability and expressions—are kind of the opposite of OO, which is all centered around mutable data structures, statements and side effects. By all means: use the features C# provides that come from the FP world and use them where it helps—LINQ, higher order functions, pattern matching, immutable data structures—but the more you try out those features to try what they can do without using OO constructs, the more you’ll find C# pulls you “back.” It’s a little like driving an Audi on the German motorway but never getting out of third gear.

My view is that 80% of the C# population today—maybe more—would be more productive and happier in F#. If you’re using LINQ, you favour composition over inheritance, and you’re excited by some of the new features in C# like records, switch expressions, tuples, and so on, F# will probably be a natural fit for you. All of those features are optimised as first-class citizens of the language, whilst things like mutability and classes are possible, but are somewhat atypical.

This also feeds back to your other question—I do fear that people will try these features out within the context of OO patterns, find them somehow limited, and leave thinking that FP isn’t worthwhile.

Let’s say I’m a C# programmer and want to get into F#. Is there any C# knowledge that will help me understand the concepts, or is it best to clear my mind of any preconceived notions before learning?

Probably the closest concept would be to imagine your whole program was a single LINQ query. Or, from a web app—imagine every controller method was a LINQ query. In reality it’s not like that, but that’s the closest I can think of. The fact that you’ll know .NET inside and out is also a massive help. The things to forget are basically the OO and imperative parts of the language: classes, inheritance, mutable variables, while loops, and statements. You don’t really use any of those in everyday F# (and believe me, you don’t need any of them to write standard line of business apps).

As an OO programmer, it’s so painful always having to worry about “the billion dollar mistake”: nulls. We can’t assume anything since we’re mutating objects all over the place and often throw up our hands and do null checks everywhere (although the language has improved in the last few versions). How does F# handle nulls? Is it less painful?

For F# types that you create, the language simply says: null isn’t allowed, and there’s no such thing as null. So in a sense, the problem goes away by simply removing it from the type system. Of course, you still have to handle business cases of “absence of a value,” so you create optional values—basically a value that can either have something or nothing. The compiler won’t let you access the “something” unless you first “check” that the value isn’t nothing.

So, you spend more time upfront thinking about how you model your domain rather than simply saying that everything and anything is nullable. The good thing is, you totally lose that fear of “can this value be null when I dot into it” because it’s simply not part of the type system. It’s kind of like the flow analysis that C# 8 introduced for nullability checks—but instead of flow analysis, it’s much simpler. It’s just a built-in type in the language. There’s nothing magical about it.

However, when it comes to interoperating with C# (and therefore the whole BCL), F# doesn’t have any special compiler support for null checks, so developers will often create a kind of “anti-corruption” layer between the “unsafe outside world” and the safe F# layer, which simply doesn’t have nulls. There’s also work going on to bring in support for the nullability surface in the BCL but I suspect that this will be in F# 6.

F#, and functional programming in general, emphasizes purity: no side effects. Does F# enforce this, or is it just designed with it in mind?

No, it doesn’t enforce it. There’s some parts of the language which make it obvious when you’re doing a side effect, but it’s nothing like what Haskell does. For starters, the CLR and BCL don’t have any notion of a side effect, so I think that this would difficult to introduce. It’s a good example of some of the design decisions that F# took when running on .NET—you get all the goodness of .NET and the ecosystem, but some things like this would be challenging to do. In fact, F# has a lot of escape hatches like this. It strongly guides you down a certain path, but it usually has ways that you can do your own thing if you really need to.

You still can (and people do) write entire systems that are functionally pure, and the benefits of pure functions are certainly something that most F# folks are aware of (it’s much easier to reason about and test, for example). It just means that the language won’t force you to do it.

What is your one piece of programming advice?

Great question. I think one thing I try to keep in mind is to avoid premature optimisation and design. Design systems for what you know is going to be needed, with extension points for what will most likely be required. You can never design for every eventuality, and you’ll sometimes get it wrong, that’s life—optimise for what is the most likely outcome.

To read the entire interview, head on over to my site.

🌎 Last week in the .NET world

🔥 The Top 3

📢 Announcements

📅 Community and events

😎 ASP .NET / Blazor

🚀 .NET Core

⛅ The cloud

📔 C#

📗 F#

🔧 Tools

📱 Xamarin

🎤 Podcasts

🎥 Videos

.NET Stacks