Hi my blog readers! I'm now back joining the F# Advent tradition! In this Advent 2024, I discuss some of What's new in F# 9..0 in .NET 9.0 only, and the dotnet restore audit behavior in .NET SDK.
I'm sure that we had enjoyed watching dotnetConf 2024 and also as usual, the launch of .NET new release annually, now it is .NET 9.0!
This release has an odd major version, it means a STS release, not the long term as the even major version. This means the .NET 9.0 has short term sipport release. The STS always has 1.5 year support, and the LTS has 3 years of support.
I personally think although it is STS release, this .NET 9.0 is quite important release. Not just it has all of the newer features of almost all languages that comes with .NET (C# and F#, unfortunately VB is not developed anymore since .NET 6.0) but it has many notable breaking changes.
Let's start discussing what's new on F# 9.0 first.
What's new in F# 9.0 in .NET 9.0
NOTE:
- I put what's new of F# in .NET 9.0 only instead of just what's new in F# 9.0 because I want to emphasize what's new in F# within .NET 9.0 SDK, not for F# that runs on other TFM such as .NET Standard. Some of the features can run on .NET 9.0 TFM and .NET Standard, as I'll give a marking on that features with different text color.
- Emphasize on .NET SDK means that it affects project system as well, not just compilers.
This is the list of what's new in F# 9:
- Nullable reference types
- Discriminated union (auto generated) .Is* properties
- Partial active patterns can return bool instead of unit option [netstandard2.0]
- Prefer extension methods to intrinsic properties when arguments are provided [netstandard2.0]
- Performance improvements
- Improvements on tooling
You can have the full list of these new features in F# 9 in the official What's new in F# 9 page:
https://learn.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-9
On those new features, to me the notable new features are nullable reference types and performance improvements.
Nullable reference types
I'm going to discuss Nullable reference types first. This is one of the notable new features, but it really needs the later support of .NET SDK, especially the Nullable in the fsproj as we already have in C# and VB projects to support Nullable.
Therefore, to enable this successfully in F# project, you have to set Nullable to enable. This is important, because by default in new F# project in .NET 9.0, the Nullable is not enabled.
For example, create new F# console project using command prompt:
dotnet new console -lang F# -n SampleFSharpConsole
This is the content of that new SampleFSharpConsole.fsproj:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net9.0</TargetFramework> </PropertyGroup> <ItemGroup> <Compile include="Program.fs"> </Compile> </ItemGroup> </project>
Fortunately the same Nullable is also the same in F# project. For example:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net9.0</TargetFramework> <Nullable>enable<Nullable/> </PropertyGroup> <ItemGroup> <Compile include="Program.fs"> </Compile> </ItemGroup> </project>
While this may sound OK on some projects that target .NET Standard 2.0, please bear in mind that it's not about the runtime, it's about the SDK support that validate the Nullable setting that starts from .NET 6.0 SDK. This SDK specific of Nullable setting also affect the compilation and language support for Nullable reference types, because by enabling this Nullable means that the compiler will always check for nullable reference syntax in F#.
For example:
The new syntax of nullable in the example of List of nullable string in F# is:
List<string | null>
and that syntax is semantically (roughly) equivalent in the C# syntax:
List<string?>
NOTE: the type may be interchangeable as long as the or (|) syntax is used to define null after the type.
As this may seem to enable compilation only, but nullable reference types are often closely associated when there's a nullabillity check at runtime, as it is more apparent in C# nullable ref types.
For more detailed information of the nullable syntax with samples, see also this official blog:
https://devblogs.microsoft.com/dotnet/nullable-reference-types-in-fsharp-9/
Performance enhancements
The F# performance enhancements in this release is quite noteworthy, as this problem has been there even before .NET Core was released. It has been there since the .NET Core 1.0 (and also in .NET Framework too).
The most interesting perf improvements are improvements when comparing objects using IEqualityComparer. In this space, some improvements by community PR in the F# repo was not quite correctly accepted, as the history is being nicely written in this official blog:
F# developer stories: how we've finally fixed a 9-year-old performance issue - .NET Blog
The detail of the history of the perf improvement to fix the perf issue are very interesting, I suggest you to read the blog to get the actual context.
The one improvement that's worth mentioning in that blog is the removal of CLR's box statement. This removal, although it is on the IL level, is not quite trivial but it does reduce CPU overhead due to boxing and allocating some heap (as object that is result of boxing). This is why you see the benchmark result is very promising since it is better: faster and almost no memory allocation on most cases.
But on some cases, these optimizations may not be applied, especially if some optimization is already available. Which is why these solution was agreed to be chosen in the F#'s Faster equality PR on GitHub, and the reasons are:
This is why I think this is very important. I will have a follow up blog after this to discuss this perf improvements in detail, including combined sample with discussion of Nullable reference type above.
Now, let's get on the broader part of .NET, the dotnet restore audit of nuget packages!
The dotnet restore always audits security vulnerabilities on the nuget packages referenced
But that comes at a price: the problematic nuget package is not just the one reported, the parent transitive dependency are also reported, and this will bring some problems such as confusions and may also lead to unexpected compile error.
In .NET 9.0 original release SDK (9.0.100), the default behavior of this audit is "all", this means the parent references are also reported. Now, the .NET 9.0.101 SDK changes that to "direct".
Unfortunately, these changes may not be apparent immediately on Visual Studio, even if you are using the patched 17.12.3. I have explained this on my .NET weekly video EPS 24:
A quick sample of this is simple: we could add a nuget package that has vulnerability problem on some version.
On the existing F# console project, let's add the log4net version 2.0.9 package like those discussed in my video:
Then run dotnet restore on that against 9.0.101 SDK, you will get this:
But when you use 9.0.100 SDK, you will get this instead:
The good thing about this is the behavior is consistent across all projects, including C#, VB and F# projects. Therefore I suggest you to update your VS 2022 as soon as possible to at least 17.12.3 and also ensure you will use .NET 9.0 SDK version 9.0.101 at least.
For more information about this dotnet restore audit, read this official .NET 9,0 breaking changes:
Breaking changes in .NET 9 | Microsoft Learn
Also read this related VS 2022 bug that is fixed in 17.12.3 as well:
That's all, friends! This wraps up my F# Advent 2024.
Also if you like my .NET Weekly video above, please like and subscribe to my channel at: https://www.youtube.com/@visualstudio_indonesia
No comments:
Post a Comment