Skip to content

Instantly share code, notes, and snippets.

@KathleenDollard
Forked from vzarytovskii/fsharp7.md
Created November 1, 2022 16:22
Show Gist options
  • Save KathleenDollard/a3eab36ad3855b8e4c74555573d606e6 to your computer and use it in GitHub Desktop.
Save KathleenDollard/a3eab36ad3855b8e4c74555573d606e6 to your computer and use it in GitHub Desktop.
fsharp7.md
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:

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.

Making working with SRTPs easier

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 ((+) or op_Addition)
  • A DivideByInt static method, which takes a T and an int and returns a T
  • A static property named Zero that returns a T
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

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

Required properties checking

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

Reference assemblies support

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 your fsproj (<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

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 and ILLink.LinkAttributes.xml for FSharp.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.

General Improvements in .NET 7

TODO

General Improvements in Visual Studio

TODO

What's next

TODO

Team news

TODO

Thanks and Acknowledgments

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.

Contributor Showcase

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.

Contributor photo

TODO: Janusz and Florian

Contributor photo

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment