A Peek into F# 4.1

Later this year, we’re going to ship a new version of Microsoft’s tools for F#. This will include support for F# 4.1, featuring important incremental improvements to the language that have been developed in conjunction with F# users and contributors. Our tools will also include a cross-platform, open-source F# 4.1 compiler toolchain for .NET Framework and .NET Core, suitable for use on Linux, macOS/OS X, and Windows. We are also updating the Visual F# IDE Tools for use with the next version of Visual Studio.

The Visual F# Tools for F# 4.1 will be updated to include support for editing and compiling .NET Core and .NET Framework projects. The Visual F# Tools will also include incremental fixes and integration with the new Visual Studio installation process. Additionally, we are currently working towards support for Roslyn Workspaces in the Visual F# Tools. By using Roslyn Workspaces, the Visual F# Tools will “plug in” to tooling innovations made in Visual Studio and be able to offer a more modern editing experience.

In this blog post, we explore what we plan to ship in more detail. For followers of our primary GitHub repository these specifics will already be familiar, however we thought it useful to bring them together into a single post. This post doesn’t cover the many generic improvements to Visual Studio, .NET and Xamarin which F# aso benefits from.

Finally, we are partnering with the F# community, including other groups at Microsoft, to ensure that F# 4.1 support is rolled out across the wide range of tooling available for F# 4.1. This includes support in Visual Studio Code, Xamarin Studio, and the popular Visual F# Power Tools for Visual Studio.

Support for the .NET Standard and .NET Core

Our compiler and scripting tools for F# 4.1 will be the first version to offer support for .NET Core. This is in addition to the existing support for .NET Framework 4.x development. When you write F# code on .NET Core today, you’re using a pre-release of F# 4.1 and this compiler toolchain.

Our tools for F# will continue to fully support .NET Framework development in a backwards-compatible way. This includes compiling existing projects created with earlier versions of Visual Studio and running existing scripts using F# Interactive (fsi.exe). Support for the latest versions of the .NET Framework is being added to these tools.

The Microsoft compiler tools for F# 4.1 are compatible with the .NET Standard, and thus are fully compatible with .NET Core and .NET Framework. The FSharp.Core library supports the .NET Standard, which allows you to use it for both .NET Core and .NET Framework development.

On Linux and macOS/OS X, the F# compiler runs as a .NET Core component, as .NET Framework is not supported on those platforms. You can also run the F# compiler as a .NET Core component on Windows.

Currently, this support is in alpha. Our aim is that the RTM support for .NET Core will coincide with the official release of F# 4.1.

Note: Until the release of .NET Standard 2.0, Type Providers will not be available on .NET Standard or .NET Core.

Getting Started with F# 4.1 on .NET Core

Note: At the time of writing, .NET Core 1.0 SDK tooling is still in preview. Details here are likely to change as that tooling evolves.

New Language Capabilities in F# 4.1

The F# 4.1 programming language introduces a number of new language capabilities focused on programmer flexibility and incremental improvements in focused areas of the language. The F# language was been developed collaboratively with F# users and the community, including many contributions from Microsoft.

Struct Tuples and Interop with C# 7/VB 15 Tuples

The tuple type in F# is a key way to bundle values together in a number of ways at the language level. The benefits this brings, such as grouping values together as an ad-hoc convenience, or bundling information with the result of an operation, are also surfacing in the form of struct tuples in C# and Visual Basic. These are all backed by the ValueTuple type.

To support the ValueTuple type and thus support interop with C# and Visual Basic, tuple types, tuple expressions, and tuple patterns can now be annotated with the struct keyword.

Here’s how looks:

Note that struct tuples are not capable of being implicitly represented as reference tuples:

Additionally, struct tuples allow for performance gains when in a situation where many tuples are allocated in a short period of time.

Struct Records

In F# 4.1, a record type can be represented as a struct with the [<Struct>] attribute. This allows records to now share the same performance characteristics as structs, without any other required changes to the type definition. Here’s an example:

There are some key behavioral things to note for struct records:

To use mutable fields within the record, any instance of the record must also be marked as mutable.

Cyclic references cannot be defined in a struct record.

You cannot call the default constructor for struct records, like you can with normal F# structs.

When marked with the CLIMutableAttribute, a struct record will not create a default constructor, because structs implicitly have one (though as stated above, you can’t call it from F#).

Struct Unions (Single Case)

In the spirit of more support for structs, in F# 4.1 the single case of the Union type will also be capable of being represented as a struct with the [<Struct>] keyword. Single case Union types are often used to wrap a primitive type for domain modelling. This allows you to continue to do so, but without the overhead of allocating a new type on the heap. Here’s an example:

Similar to struct records, single case struct unions have a few behavioral things to note:

You cannot have cyclic references to the same type be defined.

You cannot call the default constructor, like you can with normal F# structs.

Fixed Keyword

In .NET Intermediate Language (IL), it is possible to “pin” a pointer-typed local on the stack. C# has support for this with the fixed statement, preventing garbage collection within the scope of that statement. This is useful for native interop scenarios. This support is coming to F# 4.1 in the form of the fixed keyword used in conjunction with a use binding. Here’s an example:

There are some behavioral characteristics to note here:

The usage of fixed corresponds with a use to scope the pointer, e.g. use ptr = fixed expr.

Like all pointer code, this is an unsafe feature. A warning will occur when you use this.

Caller Info Argument Attributes

This allows for the ability to mark arguments to functions with the Caller Info attributes CallerLineNumber, CallerFilePath, and CallerMemberName. These attributes allow you to obtain information about the caller to a method, which is helpful for tracing, debugging, and creating diagnostic tools. Here’s an example:

Adding a Result Type

Similar to the Rust language Result enum type, we are adding support for a Result<'TSuccess, 'TError> type. A sibling of the Option<'T> type, Result<'TSuccess, 'TError> is being added to support the use case of wanting to consume code which could generate an error without having to do exception handling.

Only the type and three functions are being added at this time – map, mapError, and bind. These three functions will aid in composing functions which return Results. Other functionality, extending APIs in FSharp.Core, and more are under discussion in the Result type RFC Discussion.

Here’s an example:

You might ask, “why do this when you already have Option<'T>?”. This is a good question. When you consider the execution semantics of your code, Result and Option fill a similar goal when accounting for anything other than the “happy path” when code executes. Result is the best type to use when you want to represent and preserve an error that can occur during execution. Option is better for when you wish to represent the existence or absence a value, or when you want consumers to still account for an error, but you do not care about preserving that error.

Mutually Referential Types and Modules Within the Same File

This addition to the language allows for a collection of types and modules within a single scope in a single file to be mutually referential. The way this is specified is with the rec keyword in a top level namespace or module, e.g. module rec X, namespace rec Y.

Similar to the opt-in nature of the and keyword used to allow functions and types to be mutually referential, this allows you to opt-in to doing so over larger scopes.

This solves a common problem in a number of scenarios, such as needing to construct extra types to hold static methods and static values, organizing helper functions into modules, raising an exception in members of a type and expecting the exception to carry data of that same type, and so on.

Implicit “Module” Suffix on modules which share the same name as a type

With this feature, if a module shares the same name as a type within the same declaration group (that is, they are within the same namespace, or in the same group of declarations making up a module), it will have the suffix “Module” appended to it at compile-time.

This pattern, giving a module which is related to a type the same name as that type, is a common pattern which was traditionally supported by the [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix )>] attribute, as such:

Byref Returns

C# 7 is adding support for byref locals and byref returns to support better high performance scenarios. This will result in libraries which use these features. F# already has support for ref locals with ref cells, but to date has had no support for consuming or generating byref-returning methods. This support is coming in F# 4.1.

Note At the minimum, F# 4.1 will support consuming byref-returning methods. At the time of writing, ref returns are not complete for C# and the work in F# is still in very early stages.

Error Message Improvements

Lastly, there has been an incredible community-driven effort to improve error messages across the language. There are a few themes in these improvements, most notably in adding or improving suggested fixes with information the compiler already has.

Here’s a sample of one of the improvements. When compiling the following code,

Output with F# 4.0 looks this:

error FS0039: The record label 'With' is not defined.

Output with F# 4.1 will look like this:

error FS1129: The record label 'With' is not defined. Maybe you want one of the following:

Width

In this case, a suggestion is made based on record labels that the compiler already knows to be defined. These kinds of improvements are helpful for people new to F#, where the impact of more arcane error messages can negatively influence someone’s decision to keep using that language.

This is an ongoing process, and we’ll continue to see improvements to error messages even past F# 4.1. If you notice an error message that you feel is hard to understand, please create an issue on the Visual F# repository with an example and a proposed improvement! We’d love to improve error messages further.

Trying these new features out

The best way to try out F# 4.1 is to use pre-releases of the F# compiler tools for .NET Core, as described earlier in this blog post. You can also build the current Visual F# Compiler from source..

Rolling out F# 4.1

We are partnering with the F# community, including other groups at Microsoft, to ensure that F# 4.1 support is rolled out across the very wide range of tooling available for F# 4.1.

In particular:

The Xamarin team at Microsoft are actively incorporating F# 4.1 support into the F# support in Xamarin Studio.

The Mono packaging team is updating the packages available to include F# 4.1.

The F# community is integrating F# 4.1 support in the F# Compiler Service component, used by many editing and compilation tools.

We are working with the F# community to help update the F# support in the Visual F# Power Tools and ensure it works smoothly with the next release of Visual Studio.

The F# community are already actively integrating support for F# 4.1 into support for Visual Studio Code and Atom through the Ionide project.

The F# community are integrating support for F# 4.1 into many other tools, include Fable, an F# to ECMAScript transpiler, and into the F# support for Emacs and Vim.

The Visual F# Tools

Note: The timeline for this support may be Update 1 of Visual Studio “15” rather than RTM. There is no definite date at this time.

The Visual F# Tools for F# 4.1 will be updated to include support for editing and compiling .NET Standard projects, in addition to .NET Framework projects. .NET Standard is the common subset of APIs between .NET Core, .NET Framework, and Mono. To learn more about the .NET Standard, read The .NET Standard Library. The Visual F# Tools will also include incremental fixes and integration with the new Visual Studio installation process.

The final area for F# 4.1 is integrating the F# language service with Roslyn Workspaces. This will modernize the F# IDE experience in Visual Studio, making it comparable to C# and Visual Basic and opening the doors to future IDE innovation. Once this work is completed, many IDE features will “light up” automatically, such as IntelliSense filters.

For the uninitiated, a language service is a way to expose components of a compiler to tooling. For a simple example, when you “dot” into a class and see all the available methods, properties, and other types, the tooling has inspected that component of the Syntax Tree which corresponds to your code, gathered available type information, and displayed it for you. This is possible with the language service of the programming language you are using.

Because F#, the Visual F# Tools, and Roslyn are all open source, future productivity features in the F# editing experience for Visual Studio can also be added by the F# community.

Summary

The upcoming release described in this blog post is an exciting step in Microsoft’s tools for F#, featuring significant language improvements and cross-platform .NET Core support. Support for F# 4.1 is also being rolled out across the F# world. We invite you to use the preliminary versions of these language and tooling features today through alpha versions of our cross-platform compiler toolchain for .NET Core. Whether doing .NET Framework or .NET Core development, we invite you to help contribute to the development of the tools on the Visual F# GitHub repository.

As far as I can tell, with this release they made one attribute obsolete (ModuleSuffix) and added support for existing ones (CallerLineNumberAttribute, CallerFilePathAttribute, CallerMemberNameAttribute). Additionally it’s how you do in .Net 🙂

I think Dev was talking about the [] attribute, which is indeed very much obfuscating the code.
Also, the “rec” keyword seems redundant. I see no real compelling reason for it. Should be deprecated perhaps.

Ideas:
– struct attribute seems to be an opt-in restriction for the developer to enforce optimization (will optimization hold also without this attribute, when using single argument DUs ?)
– one reason for the enforcement of DU instead of having type abbreviation is, that they are different features “activated”. Logically it’s the same and actually there is nothing to discriminate, so it’s a little language mess
– in general the different types could behave more consistently, how the binary looks isnt actually relevant, when I have an enum and I want to write a method or overload an operator or want to write a to ToString method, why not. Trust the developers, they know usually, what they are doing. The language could just “fake” this by internally deciding to use an object instead for example. The main benefit is, that it will not be necessary anymore to know, how an F# object works in a C# world. One could take an opt-in attribute-approach to enforce types on C# side, whereas a plain F# developer doesnt need to care about
– type abbreviations in general are sort of strange, why one need dozens of integers for example. When there is a need for some specific dotnet type like unsigned int with overflow detection, it’s a oneliner to introduce them globally (where it’s sadly not possible to extend them, because they are marked as sealed, so we fall back to the DU case)
– maybe there are 2 main directions to handle the interoperability of F# vs. C#: either F# is fantastically abstract, so a developer explicitely needs to interoperate with C# specific classes like “delegate vs function” or optional arguments. OR the languages are kind of connected in a seemless way, that when I use an async workflow, its a function, that can be used in C# via the async/await pattern too, without library, without extra code, without gap.
– I think, some “trivial” API-functions, that are implemented as one-liner dont need to be implemented in the API, when there is no actual implementation beside one or two piped actions. This means it could be better to prefer performance optimized methods / a consistent API (same functions) for different types, f.e. an Array2D.map could be more useful for beginners, than a Seq.zip3, but ideally when one on the types List/Seq/Array supports a zip3, then the others should too.
– The difference of List/Array is probably also redundant EXCEPT performance and interoperation, but as said before: performance could be handled by compiler optimization and interoperation could be handled by either enforce an underlying C# type via annotation or having a common interface. At least when compiled, I often see F# classes, that dont look very well in a C# API and the other way around.

Just my 2, 3 cent. F# is pretty nice, but for my taste, the inconsistences / redundancies could be decreased instead of increasing them with syntax suggar. Because at my beginners stage, I was wondering, why one character like “|” or “= 0” changes a type in a way, that probably all implemented functions just have to be rewritten.