post_title | username | microsoft_alias | featured_image | categories | summary | desired_publication_date |
---|---|---|---|---|---|---|
What’s new in F# 7 |
fsharp512.png |
F#, .NET, .NET Core |
F# 7 is now released |
2022-11-15 |
We’re happy to announce the availability F# 7, shipping with .NET 7 and Visual Studio 2022. It's the next step to making it easier for you to write robust, succinct and performant code. You can get F# 7 in the following ways:
- Install the latest .NET 7 SDK
- Install Visual Studio 2022 preview
- Use .NET Interactive Notebooks in Jupyter or VS Code
TODO, UPDATE: F# 7 is about making F# simpler and more performant. This applies to language design, library, and tooling. A major goal of long-term language evolution is to remove corner cases in the language that surprise users or are unnecessary hurdles on the path to adoption. We are very pleased to have worked with the F# community in this release to make the F# language simpler, more performant, and easier to learn.
TODO, UPDATE: To learn more about F#, see The .NET Conf Focus Day on F# including the F# Bonanza, Guido van Rossum Learns F# and Starting Your F# Journey.
Note
SRTPs or Statically Resolved Type Parameters are type parameters that are replaced with an actual type at compile time instead of at run time.
As a quick refreshment on what SRTPs are, and how they're used, let's declare a function named average
that takes an array of type T
where type T
is a type that has at least the following members:
- An addition operator (
(+)
orop_Addition
) - A
DivideByInt
static method, which takes aT
and anint
and returns aT
- A static property named
Zero
that returns aT
let inline average< ^T
when ^T: (static member (+): ^T * ^T -> ^T)
and ^T: (static member DivideByInt : ^T * int -> ^T)
and ^T: (static member Zero : ^T)>
(xs: ^T array) =
let mutable sum : ^T = (^T : (static member Zero: ^T) ())
for x in xs do
sum <- (^T : (static member op_Addition: ^T * ^T -> ^T) (sum, x))
(^T : (static member DivideByInt: ^T * int -> ^T) (sum, xs.Length))
^T
here is a type parameter, and ^T: (static member (+): ^T * ^T -> ^T)
, ^T: (static member DivideByInt : ^T * int -> ^T)
, and ^T: (static member Zero : ^T)
are constraints for it.
We are making some improvements.
It's no longer required to use a dedicated type parameter character (^
), a single tick character ('
) can be used instead, compiler will decide whether it's static or generic based on the context - whether the function is `inline`` and if it has constraints.
We are also adding a new simpler syntax for calling constraints, which is more readable and easier to write:
let inline average<'T
when 'T: (static member (+): 'T * 'T -> 'T)
and 'T: (static member DivideByInt : 'T * int -> 'T)
and 'T: (static member Zero : 'T)>
(xs: 'T array) =
let mutable sum = 'T.Zero
for x in xs do
sum <- sum + x
'T.DivideByInt(sum, xs.Length)
Finally, we've added ability to declare constraints in groups:
type AverageOps<'T when 'T: (static member (+): 'T * 'T -> 'T)
and 'T: (static member DivideByInt : 'T*int -> 'T)
and 'T: (static member Zero : 'T)> = 'T
And a simpler syntax for self-constraints, which are constraints that refer to the type parameter itself:
let inline average<'T when AverageOps<'T>>(xs: 'T array) =
let mutable sum = 'T.Zero
for x in xs do
sum <- sum + x
'T.DivideByInt(sum, xs.Length)
Simplifed call syntax also works with instance members:
type Length<'T when 'T: (member Length: int)> = 'T
let inline len<'T when Length<'T>>(x: 'T) =
x.Length
We believe that those changes will make working with SRTPs easier and more readable.
Static abstract members support in interfaces is a new feature of .NET 7. One notable application is generic math.
We are adding a way of declaring, calling and implementing static abstract members in interfaces.
First, let's declare an interface with static abstract member:
type IAddition<'T when 'T :> IAdditionOperator<'T>> =
static abstract op_Addition: 'T * 'T -> 'T
Note The code above will produce
FS3535
warning -Declaring "interfaces with static abstract methods" is an advanced feature. See <https://aka.ms/fsharp-iwsams> for guidance. You can disable this warning by using '#nowarn "3535"' or '--nowarn:3535'.
Next, we can implement it:
type IAddition<'T when 'T :> IAddition<'T>> =
static abstract op_Addition: 'T * 'T -> 'T
type Number<'T when IAddition<'T>>(value: 'T) =
member _.Value with get() = value
interface IAddition<Number<'T>> with
static member op_Addition(a, b) = Number(a.Value + b.Value)
This will allow us to write generic functions that can be used with any type that implements IAddition
interface:
let add<'T when IAddition<'T>>(x: 'T) (y: 'T) = 'T.op_Addition(x,y)
or in the operator form:
let add<'T when IAddition<'T>>(x: 'T) (y: 'T) = x + y
This is a runtime feature and can be used with any static methods or properties, you can see a few more examples below.
type ISinOperator<'T when 'T :> ISinOperator<'T>> =
static abstract Sin: 'T -> 'T
let sin<'T when ISinOperator<'T>>(x: 'T) = 'T.Sin(x)
This feature can also be used with BCL built-in types (such as INumber<'T>
), for example:
open System.Numerics
let sum<'T, 'TResult when INumber<'T> and INumber<'TResult>>(values: 'T seq) =
let mutable result = 'TResult.Zero
for value in values do
result <- result + 'TResult.CreateChecked(value)
result
C# 11 introduced new required modifier for properties, F# 7 supports consuming classes with required properties and enforcing the constraint:
Consider the following data type, defined in C# library:
public sealed class Person
{
public required string Name { get; set; }
public required string Surname { get; set; }
}
When using from F# code, compiler will make sure required properties are getting properly initialized:
let person = Person(Name = "John", Surname = "Doe")
The code above will compile correctly, but if we try to omit any of the required properties, we will get a compile-time diagnostic:
FS3545: The following required properties have to be initalized:
property Person.Surname: string with get, set
Starting F# 7, compiler can generate reference assemblies.
A reference assembly only has references for what it needs in the API surface. The real assembly may have additional references related to specific implementations.
You can generate reference assemblies by:
- Adding
ProduceReferenceAssembly
MSBuild project property to yourfsproj
(<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
) file or msbuild flags (/p:ProduceReferenceAssembly=true
). - Using
--refout
or--refonly
compiler options.
We are looking forward to your feedback on this feature.
Other changes in F# 7 include:
- Fixes in resumable state machines codegen for the tasks builds.
- Better codegen for compiler-generated side-effect-free property getters.
- Reflection-free codegen flag for F# compiler `--reflectionfree":
- Skip emitting "%A" ToString implementation for records, unions and structs.
- Trimmabillity improvements for F# - added
ILLink.LinkAttributes.xml
andILLink.LinkAttributes.xml
forFSharp.Core
, which allows trimming compile-time resources and attributes for runtime-only FSharp.Core dependency. - ARM64 platform-specific compiler and ARM64 target support in F# compiler.Dependency manager
#r
caching support. - Parallel type-checking and project-checking support (experimental, can be enabled via VS setting, or by tooling authors).
- Miscellaneous bugfixes and improvements.
TODO
TODO
TODO
TODO
F# is developed as a collaboration between the .NET Foundation, the F# Software Foundation, their members and other contributors including Microsoft. The F# community is involved at all stages of innovation, design, implementation and delivery and we're proud to be a contributing part of this community.
Many people have contributed directly and indirectly to F# 7, including all who have contributed to the F# Language Design Process through fsharp/fslang-suggestions and fsharp/fslang-design repositories.
Adam Boniecki, Brett V. Forsgren, Don Syme, Kevin Ransom, Petr Pokorný, Petr Semkin, Tomáš Grošup, Vlad Zarytovskii, Will Smith contributed directly at Microsoft.
Florian Verdonck made 72 PRs, including work on parallel type-checking improvements, tons of improvements to the syntax trees, which benefit all tooling in general and Fantomas source code formatter in particular.
Eugene Auduchinok made 26 PRs including bunch of improvements and optimizations in parser, IL reading, completions, compiler service caching and more.
Our new contributor Edgar Gonzalez 🥳 made 17 PRs tons of improvements and fixes around attributes processing, RequiredQualifiedAccess & lower-cased DU cases relaxations and whole bunch of bugfixes.
kerams made 13 PRs for records, types, unions, enums, and lambda variables completions, IL gen improvements, and more.
Other direct contributors to the dotnet/fsharp repository in the F# 7.0 time period include teo-tsirpanis, DedSec256, baronfel, leolorenzoluis, dawedawe, tmat, marcin-krystianc, cartermp, pirrmann, safesparrow, omppye, Happypig375, ncave, thinkbeforecoding, MichaelSimons, tboby, mmitche, nosami, jonfortescue, smoothdeveloper, abelbraaksma, uxsoft .
Many other people are contributing to the rollout of F# 7 and .NET 7 in Fable, Ionide, Bolero, FSharp.Data, Giraffe, Saturn, SAFE Stack, WebSharper, FsCheck, DiffSharp, Fantomas and other community-delivered technologies.
In this and future announcements, we will highlight some of the individuals who contribute to F#. This text is written in the contributors’ own words:
I'm Edgar Gonzalez, and I live between Albacete(Spain) and London(Uk), where I'm a Mobile Developer at Fund Ourselves.
I'm new to the .NET ecosystem. Previously I was a member of the Spanish Paratroopers Brigade "Almogávares" VI, where I served as a Corporal first class (2004-2017).
I started programming five years ago, interested in mobile development with C#, and around the same time, I was introduced to F# by Frank A. Krueger https://twitter.com/praeclarum using Xamarin.iOS
Last year I moved to F#, and during my journey realized that there is room for improvement regarding compiler error reporting, so I decided to get involved and help make F# simple for newcomers like me.
Why do I like F#? it has a clean and lightweight syntax, is expression-oriented, exhaustive pattern matching, and discriminated unions.
I look forward to continuing to help F# to be even better, focusing on making it easy for newcomers.
Thanks to Timothé Larivière [https://twitter.com/Tim_Lariviere] and Florian Verdonck [https://twitter.com/verdonckflorian] for helping me get started with open source.
TODO: Janusz and Florian