Detailed Release Notes for the F# May 2009 CTP Update and Visual Studio 2010 Beta1 releases

The main announcement for this release is here.

 

Summary

  • Release
    • F# is integrated into Visual Studio 2010 Beta1
    • F# in Visual Studio2010 can build applications for .NET 4.0 Beta1
    • Updated F# CTP for Visual Studio 2008 and .NET 2.0/3.0/3.5
    • F# PowerPack is a separate download for Visual Studio 2010
    • F# binaries compiled with this new release are incompatible with binaries from previous releases, so must be recompiled. Binary compatibility for F# is an aim of the RTM release of Visual Studio 2010.
  • F# Language
    • #light is the default
    • Support for calling and exposing .NET ParamArray parameters (‘params’ in C#)
    • Performance improvement for Sequence Expressions
    • Units of measure support for signed integral types
    • Support for custom numeric types (123X, 1.0R, etc.)
    • Overloading tie-breaker rules result in fewer ambiguity errors when calling overloaded members
    • Attributes can be placed on implicit class constructors
    • Let bindings and implicit constructor parameters in type definitions only generate fields when necessary
    • Curried members are generated as un-curried .NET members
    • Support for InternalsVisibleTo
    • Support for all Assembly*Attributes
    • Type augmentation members are now only intrinsic when declared in the same namespace and file
    • The ‘null’ literal now generalizes
    • Structs are now analyzed for nullness based on their contents
    • F# record and union types implement the new .NET 4.0 interfaces IStructuralEquatable and IStructuralEquality
    • Indexing and Slicing is now supported up to Rank 4 arrays
  • F# Core Libraries
    • FSharp.Core.dll API naming standardization resulting in a handful of API changes
    • Silverlight2 version of FSharp.Core.dll available in the CTP Update
    • RequiresQualifiedAccess applied to many of the modules in FSharp.Core.dll to prevent opening the module in client code
    • BigInteger/BigRational changes

o Structured Formatting Customization for %A and FSI display

    • ResizeArray module moved to F# Power Pack
    • IStructuralHash removed, IStructuralEquatable and IStructuralComparable added
    • [.Net 4.0] FSharp.Core.dll is SecurityTransparent and can be used in partial trust
    • [.Net 4.0] F# uses new .NET4.0 Lazy and Tuple types
    • [.Net 4.0] Async.StartAsTask and Task<>.AwaitValue convert between F# Async and .NET4.0 Task
    • [.Net 4.0] Array.Parallel.* functions provide data parallel versions of common array operations
  • F# Compiler

    • Fsc.exe command line compiler options cleanup and standardization
    • Passing mscorlib and FSharp.Core.dll explicitly is supported, and defines the targeted runtime
    • Optimizations for Sequence Expressions
    • Optimizations for Tailcalls
    • Optimizations for Curried Methods
    • Optimizations for Class Representations
  • Visual Studio

    • Error List populated by results of Build as well as background errors
    • Performance improvements in F# Visual Studio language service
    • Many improvements to debugging in Visual Studio, including breakpoint setting and data visualization.
    • More reliable F# Intellisense in the face of incomplete or incorrect code
    • Improved project build and rebuild performance and reliability
    • QuickInfo on #r shows details of the referenced assembly
  • F# Interactive

    • F# Interactive displays all bound values
    • Simplified command line format for fsi.exe makes it easy to execute simple F# scripts with “fsi.exe script.fsx a1 a2”
    • Fsi.exe emits debug symbols, so can be attached to, to debug a running script
  • F# PowerPack

    • Additional Async utility functions
    • An API for accessing F#-specific metadata from F# assemblies - FSharp.PowerPack.Metadata.dll
  • Miscellaneous

    • Beta F# Language and Compiler documentation on MSDN
    • Deletion of features deprecated in previous F# releases
    • Plus many, many more minor fixes and improvements

F# Language

 

#light is the default

All F# source files now use the whitespace-aware #light syntax by default. This may be turned off with “#light off” at the top of a source file.

 

.NET ParamArray parameters (‘params’ in C#)

F# can call methods with ParamArray parameters defined in other .NET languages such as:

System.Console.WriteLine("a","b","c","d","e","f")

// Results in the following call:

// System.Console.WriteLine("a", [|"b";"c";"d";"e";"f"|])

F# types can also expose ParamArray parameters themselves using System.ParamArrayAttribute:

open System

type MyType() =

    member this.M([<ParamArray>] args : string[]) =

        args |> Array.iter (printfn "%A")

 

Performance of Sequence Expressions

Sequence expressions are now compiled to state machines, instead of as a collection of API calls. This results in improved performance of F# code which works with seq. Many of the Seq.* library functions also have improved performance as a result of this change.

 

Units of measure support for signed integral types

Units of measure may now be applied to integral types such as int and int64, as well as existing floating point types such as float and decimal.

[<Measure>]

type clicks

let x = 64<clicks>

Also, a few bugs fixes to enable definitions of OO types annotated with measures to have the full range of overloaded operators (e.g. Vector, Matrix etc.).

 

Support for custom numeric types (123X, 1.0R, etc.)

It is now possible to bind numeric literals to custom numeric types. Custom numeric literals, with a numeric constant followed by a single letter, are treated as syntactic sugar for API calls.

In particular, 123X is now syntactic sugar for

     NumericLiteralX.FromInt32(123)

Likewise

     NumericLiteralX.FromZero()

      NumericLiteralX.FromOne()

      NumericLiteralX.FromInt32(6)

      NumericLiteralX.FromInt64(…)

      NumericLiteralX.FromString(…)

Depending on the input constant.

 

Type augmentation members are now only intrinsic when declared in the same namespace and file

In previous version, intrinsic type augmentations could be defined in nested modules in a file. This is no longer permitted and these items become extension members instead

 

The ‘null’ literal now generalizes

For example

let x = null

Is now given generic type, with a constraint that the type must support null

 

Structs are now analyzed for nullness based on their contents

For example, the following type will not satisfy the nullness constraint, because it contains a field that is a discriminated union type:

type DiscUnion = Fall | Winter | Spring | Summer

[<Struct>]

type StructType =

    val val1 : int

    val val2 : string

    val val3 : DiscUnion

    member v.Val1P = v.val1

    member v.Val2P = v.val2

    member v.Val3P = v.val3

 

F# record and union types implement the new .NET 4.0 interfaces IStructuralEquatable and IStructuralEquality

A copy of these types is included in the FSharp.Core.dll for .NET 2.0

 

Indexing and Slicing is now supported up to Rank 4 arrays

In previous versions slicing was only supported for Rank 1 and 2 arrays

 

F# Library

Standard Library Naming Conventions

The naming conventions adopted for the F# library are as follows:

o All .NET and F# OO code uses PascalCase according to existing .NET guidelines

o The F# functional programming operators such as List.map are for use in F# internal implementation code. This kind of code uses camelCase for operator names

o Underscores should not be used except for the Module.to_type and Module.of_type pattern. In Beta1 you will only see underscores for this pattern

As a result, the F# functional programming operators now uniformly use camelCase for compound words. For example

List.map

List.averageBy

List.sumBy

Standard, Simpler Operator Names

One of the most important steps we have taken in evolving the F# library design is to ensure uniformity and regularity between the functional programming operator names and signatures used in the F# library, e.g. those appearing in the List, Array, Seq, Map, Set, String and Option modules. This release achieves this, based on the standard operator names shown in the tables below.

Achieving this uniformity required two breaking changes without a matching deprecation warning:

o List.sort no longer takes a comparer. Instead List.sortWith should be used instead if a custom comparer is needed. This change was made to ensure uniformity and simplicity in the common case

o Array.sort now returns a copy of the resulting array. If you wish to perform and in-place sort, you should use Array.sortInPlace or Array.sortInPlaceWith.

If you need to quickly restore compatibility with the previous version of F#, you can add the following declarations to your code:

                module List =

                    let sort f x = List.sortWith f x

                module Array =

                    let sort f x = Array.sortInPlaceWith f x

In addition, we have chosen the standard operator names fold, foldBack, scan and scanBack to represent the operators that were previously variously called fold, fold_left, fold_right, scan, scan_left and scan_right in the F# library, and foldl, foldr, scan and scanr in other functional language implementations. The old operator names continue to be available with a compatibility warning.

While we understand that the introduction of the new name foldBack departs from any specific functional language tradition, we have canvassed this choice with a range of users and have trialed it extensively, and the results have been satisfying. In particular, the argument order used for this function strongly associates the operation with the process of accumulating results from the end of an ordered data structure back to the front.

Query Operators

Operator Name

Typical signature

C.choose f c

('T -> option<'U>) -> C<'T> -> C<'U>

C.collect f c

('T -> C<'U>) -> C<'T> -> C<'U>

C.exists f c

('T -> bool) -> C<'T> -> bool

C.exists2 f c1 c2

('T -> 'U -> bool) -> C<'T> -> C<'U> -> bool

C.find f c

('T -> bool) -> C<'T> -> 'T

C.filter f c

('T -> bool) -> C<'T> -> C<'T>

C.forall f c

('T -> bool) -> C<'T> -> bool

C.forall2 f c1 c2

('T -> 'U -> bool) -> C<'T> -> C<'U> -> bool

C.map f c

('T -> 'U) -> C<'T> -> C<'U>

C.map2 f c1 c2

('T1 -> 'T2 -> 'U) -> C<'T1> -> C<'T2> -> C<'U>

C.mapi f c

(int -> 'T -> 'U) -> C<'T> -> C<'U>

C.mapi2 f c1 c2

(int -> 'T1 -> 'T2 -> 'U) -> C<'T1> -> C<'T2> -> C<'U>

C.partition f c

('T -> bool) -> C<'T> -> C<'T> * C<'T>

C.pick f c

('T -> option<'U>) -> C<'T> -> 'U

C.tryFind f c

('T -> bool) -> C<'T> -> 'T option

C.tryFindIndex f c

('T -> bool) -> C<'T> -> int option

C.tryPick f c

('T -> option<'U>) -> C<'T> -> option<'U>

Zip, Unzip Operators

Operator Name

Typical signature

C.unzip c

C<'T1 * 'T2> -> C<'T1> * C<'T2>

C.unzip3 c

C<'T1 * 'T2 * 'T3> -> C<'T1> * C<'T2> * C<'T3>

C.zip c

C<'T1> * C<'T2> -> C<'T1 * 'T2>

C.zip3 c

C<'T1> * C<'T2> * C<'T3> -> C<'T1 * 'T2 * 'T3>

Folding, Scanning, Reducing Operators

C.fold f state c

('State -> 'T -> 'State) -> 'State -> C<'T> -> 'State

C.fold2 f state c1 c2

('State -> 'T1 -> 'T2 -> 'State) -> 'State -> C<'T1> -> C<'T2> -> 'State

C.foldBack f state c

('T -> 'State -> 'State) -> C<'T> -> 'State -> 'State

C.foldBack2 f state c1 c2

('T1 -> 'T2 -> 'State -> 'State) -> C<'T1> -> C<'T2> -> 'State -> 'State

C.reduce f c

('T -> 'T -> 'T) -> C<'T> -> 'T

C.reduceBack f c

('T -> 'T -> 'T) -> C<'T> -> 'T

C.scan f c

('State -> 'T -> 'T) -> 'State -> C<'T> -> 'T

C.scanBack f c

('T -> 'State -> 'T) -> C<'T> -> 'State -> 'T

Iteration Operators

Operator Name

Typical signature

C.iter f c

('T -> unit) -> C<'T> -> unit

C.iter2 f c1 c2

('T -> 'U -> unit) -> C<'T> -> C<'U> -> unit

C.iteri f c

(int -> 'T -> unit) -> C<'T> -> unit

C.iteri2 f c1 c2

(int -> 'T -> 'U -> unit) -> C<'T> -> C<'U> -> unit

Aggregation Operators

Operator Name

Typical signature

C.average c

C<'T> -> 'T

   when 'T : (static ( + ) : 'T * 'T -> 'T)

    and 'T : (static DivideByInt : 'T * int -> 'T)

    and 'T : (static Zero : 'T)

C.averageBy c

('T -> 'U') -> C<'T> -> 'U

   when 'U : (static ( + ) : 'U * 'U -> 'U)

    and 'U : (static DivideByInt : 'U * int -> 'U)

    and 'U : (static Zero : 'U)

C.sum c

C<'T> -> 'T

   when 'T : (static ( + ) : 'T * 'T -> 'T)

    and 'T : (static Zero : 'T)

C.sumBy c

('T -> 'U') -> C<'T> -> 'U

   when 'U : (static ( + ) : 'U * 'U -> 'U)

    and 'U : (static Zero : 'U)

Sorting Operators

Operator Name

Typical signature

C.sort c

C<'T> -> C<'T>

C.sortBy c

('T -> 'Key) -> C<'T> -> C<'T>

C.sortWith c

('T -> 'T -> int) -> C<'T> -> C<'T>

Operators for Linearly Ordered and Indexed Types (strings, lists, arrays etc.)

Operator Name

Typical signature

C.append c1 c2

C -> C -> C

C.concat cs

seq<C> -> C

C.empty

C

C.init f n

(int -> 'T) -> int -> C<'T>

Uppercase type variable names.

You will notice that the F# library reports upper case names for F# type variables, e.g. the signature for List.map is shown using 'T for the name of the type variable. This change was made after reviewing the experience of importing generic code from other .NET languages, and we now encourage the use of upper case names for user-defined type variables according to .NET programming standards,. That said, we very much understand that lower case type variable names will remain very common in F# programming, and indeed, the names of type-inferred type variables continue to be lower case in this release.

Array2 --> Array2D

Array3 --> Array3D

Often requested!

BigInteger/BigRational

F# BigInteger has been aligned with the new .NET4.0 BigInteger type. On .NET 2.0, a matching BigInteger implementation is included in FSharp.Core.dll.

The BigRational type has been moved into the F# PowerPack.

 

Structured Formatting Customization

Two tools for customizing structured formatting of values with %A or in the F# Interactive have been added to this release.

1. Width and node count parameters on %A format

%<width>.<node-count>A

        A width of 0 will mean “no line splitting”, i.e. 1-dimensional layout.

2. Extensibility via an attribute:

[<StructuredFormatDisplay("matrix {StructuredDisplayAsArray}")>]

type Matrix<'T>(width,height,generator:int->int->'T) =

   

    ///...Matrix members...

   

    member private this.StructuredDisplayAsArray =

        Array2D.init width height generator

This attribute is used to mark how a type is displayed by default when using '%A' printf formatting patterns and other two-dimensional text-based display layouts. In version 1 of F# the only valid values will be of the form PreText {PropertyName} PostText. The property name indicates a property to evaluate and to display instead of the object itself.

 

As a library developer:

o Override ToString for simple types

o Consider implementing DebuggerDisplay

§ if you expect users to often see this type in the debgugger

§ AND overriding ToString is not adequate for this purpose

o Consider implementing StructuredFormatDisplay if

§ you expect users to see this type in a console or other textual display

§ and the default results of “printf %A” for the type is not adequate

§ and overriding ToString is not adequate

§ It is OK to implement all three

As a user:

o Use %A for structured formatting

o Use fsi.AddPrinter to make adhoc changes to how FSI displays data

 

  [.Net 4.0] F# uses new .NET4.0 Lazy and Tuple types

F# compiled for .NET4.0 uses the new System.Tuple and System.Lazy types in place of F#-specific types.

 

[.Net 4.0] Converting between F# Async and .NET4.0 Tasks

F# Async workflows can be used to compose together .NET4.0 Tasks, using the Task<’T>.AsyncValue and Async.StartAsTask functions.

async { // Await the value from some future we've been given
// This is effectively "ContinueWith"
let! v1 = someFuture.AsyncValue
... }

async { // Set a sub-async program running as a future
let! subtask = Async.StartChildAsTask ( async { ... } )
// Await the value from the future (don't block the thread!)
// This is effectively "ContinueWith"
let! v1 = subtask.AsyncValue
... }

let mytask = async { ... } |> Async.StartAsTask

 

[.Net 4.0] Array.Parallel

Data parallel versions of some of the common Array operations are available in the Array.Parallel module. These can be used to quickly parallelize key loops in F# code. The performance profile of these operations is derived from the System.Threading.Parallel.For API in .NET4.0.

let random = new System.Random()

let numPoints = 10000000

let points = Array.init numPoints (fun _ -> random.NextDouble(), random.NextDouble())

let numInCircle =

    points

    |> Array.Parallel.choose (fun (x,y) ->

        if x*x + y*y <= 1.0 then Some (x,y) else None)

    |> Array.length

let pi = 4.0 * float numInCircle / float numPoints

 

Some deprecated functionality has been removed

F# Compiler

 

Compiler command line option normalization

 

The command line options for the F# compiler have been further aligned with standard .NET compiler practice. Here are a few notable changes:

· All long-form compiler options taking parameters now require a ‘:’, as in --target:exe

· Tailcalls is now set based on a separate –tailcalls[+/-] flag, which is on by default

· Cross-module optimization is now set based on a separate --crossoptimize[+/-] flag, which is on by default

To see the full list of compiler options, use fsc.exe -? .

 

Optimizations for Sequence Expressions

Sequence expressions now compile to a much more efficient form, e.g.

seq { for i in 0 .. 100 do

         for j = i .. 100 do

             yield i + j }

will compile to a state machine object that will yield its values on demand.

 

Optimizations for Tailcalls

On some implementations of the CLI, normal calls can be more efficient than tailcalls. An optimization is now applied to determine if a function is “closed” in the sense that it never takes any tailcalls outside a finite non-recursive callgraph. If so, the use of tailcalls is suppressed.

 

Optimizations for Curried Methods

A curried member looks like this:

type C() =

    static member Sum a b = a + b    

In previous implementations of F# curried members were compiled less efficiently than non-curried members. This has now been changed. However, there are now some small restrictions on the definition of curried members:

Ø curried members may not be overloaded

Ø some definitions of curried members may need to be adjusted to add the correct number of parameters to the definition

Optimizations for Class and Struct representations

For class and struct types, arguments and non-function let-bindings that are not used in either the members of a type or a function binding are now optimized away and become locals to the generated constructor. Likewise, function bindings now get represented as instance members.

For example, given this type:

type C(x:int,y:int) =

    let z = x + y

    let f w = x + w

    member x.Z = z

    member x.Add(w) = f w

The input “y” is only used during construction, and no field will be stored for it. Likewise the function “f” is now represented as a member rather than a function-valued field.

F# PowerPack

 

Additional Async utility functions

The following Async utility functions have been added to the PowerPack:

· Async.AwaitEvent

· Async.AsBeginEnd

· AsyncResult<_>

· AsyncResultCell<_>

· System.Net.WebClient.AsyncDownloadString

Some deprecated functionality has been removed

FSharp.PowerPack.Metadata.dll

The API in this assembly provides access to the F#-specific metadata. This can be used to get information about F# notions such as type abbreviations, units of measure, and currying or parameters.

 

#r "FSharp.PowerPack.Metadata.dll"

open FSharp.PowerPack.Metadata

let assm = FSharpAssembly.FromFile(@"FSharp.Core.dll")

let entities = assm.Entities

let allAbbreviations =

    [ for e in entities do

        if e.IsAbbreviation
then yield e.Name ]

You may need to explicitly load some dependent DLLs before calling FSharpAssembly.FromFile