Constructors as first-class functions
As a functional-first language, F# has always provided rich support for processing and manipulating function values, both curried-style functions and traditional .NET methods. One longstanding gap in that support was for constructors – there was no way to treat a type constructor as an isolated function that could be passed around and composed with other functions. This limitation was one of the few sources of boilerplate in F# code, mandating the use of small lambda functions in order to invoke constructors.
This is now resolved – constructors are now treated as first-class functions, with the same treatment as other traditional .NET methods.
So, as an example, instead of writing this:
You can now write this:
Simplified use of mutable values
Mutable values are permitted in F#, though immutability is the default. In the past, the ‘mutable’ keyword was used to designate a stack-based mutable value, and that value could be mutated using the <- operator. If the mutable value happened to be captured by a closure and a heap-based value was required, the ‘ref’ syntax was needed instead, and mutation was done via the := operator.
These distinct syntaxes led to some code inelegance and developer confusion, as it was often difficult to reason ahead of time about which approach was going to be required.
With F# 4.0, developers can now just use the ‘mutable’ keyword for all mutable values, and the compiler will take care of the rest, creating a stack-based value if possible, otherwise implicitly converting to a ref cell.
So whereas you previously needed a ‘ref’ value for ‘total’ here:
You can now use ‘mutable’ and it “just works”:
Advanced users who prefer to have explicit knowledge of the stack/heap semantics can enable warning 3180 (--warnon:3180 for fsc.exe or fsi.exe) and will be notified when a ‘mutable’ declaration has been implicitly converted to ‘ref’.
Support for high-dimensional arrays
The .NET framework supports up to 32-dimensional arrays, but in the past F# only supported use of up to rank-4 arrays. Not only were arrays of rank 5+ not possible to create and manipulate from F# code, the compiler could sometimes fail to consume external libraries which relied on high-dimensional arrays.
This is now fixed. Although there is not yet support for creating and manipulating high-rank arrays, the compiler will now properly handle these types up to rank-32.
Support for static parameters to provided methods
F# type providers were previously limited to accepting static parameters only on the top-level provided type. From there, all other provided types, methods, and properties needed to be generated based on this single set of parameters.
With F# 4.0, the type provider mechanism has been expanded to allow for static parameters on provided methods, as well.
With this change, instead of requiring users to declare multiple top-level types, each with distinct parameters, there is now an option to use a single type declaration along with parameterized usage of methods. For certain kinds of type providers, this can greatly streamline the developer experience.
A simple example is a provided “safe string” type which enforces that the number of slots in a String.Format format string matches the number of arguments passed. This can now be done with a single type declaration, and a parameterized Format method:
Slicing syntax support for F# lists
F# linked lists now support the familiar “slicing” syntax, long present for arrays, for reading elements and creating sub-lists.
Simplified usage of units of measure with printf-family functions
printf-family functions previously required developers to explicitly strip off any measure information before values could be formatted.
This requirement has been relaxed – values with measure information are now handled seamlessly.
Modified GC settings on the compiler for better performance
Not a language feature, but surely of interest to F# language developers, is a change to the GC mode used by the F# compiler. fsc.exe now uses GCLatencyMode.Batch, which gives a noticeable improvement in overall throughput, something that any F# developer will welcome.
Our testing indicates that this setting alone can provide upwards of 10% better compile performance. Click the chart below for a full-size plot of the performance data.