Partager via


Release Notes for the F# October 2009 release

 

(These notes apply to the F# October 2009 CTP update and Visual Studio 2010 Beta2)

 

Summary

  • Release
    • F# in Visual Studio2010 Beta2 can build applications for .NET 2.0/3.0/3.5/4.0 and Silverlight 2/3.
    • Updated F# CTP for Visual Studio 2008
    • F# PowerPack available as part of the CTP Update, and can be used with either CTP Update of VS2010 Beta2
  • F# Language
    • OverloadID no longer required for defining overridden methods
    • Require module/namespace at top of files
    • ‘comparison’ and ‘equality’ constraints
    • Core conversion operators use .NET standard op_Implicit/op_Explicit instead or ToInt32, etc.
    • Support for global namespace qualification, “global.A.B.C” and “namespace global”
    • More flexibility in defining yield! and return! in computation expressions using YieldFrom and ReturnFrom
    • Extension methods can participate in overload resolution
    • Support for volatile fields using [<Volatile>]
    • Support for letting F# types allow “null” using [<AllowNullLiteral>]
    • Interface implementations can now be inherited
    • Private type abbreviations (type private X = int)
    • More flexibility for list literals and subtyping
    • Recursive type inference improvement
  • F# Compiler
    • HTML doc generation moved from compiler to PowerPack
    • Discriminated Union code generation provides simpler .NET view
  • F# Core Libraries
    • Complete the under_score -> camelCase name standardization, of_list -> ofList
    • Async API Additions for Web Requests
    • Async API Additions for Reactive Applications
    • Array2D enables easily creating 2D arrays
    • Observable module
    • Version-stable serialization of Map and Set
    • [.NET 4.0] Lazy, CancellationToken, BigInteger in .NET4.0
    • CompiledName used to ensure .NET view is C#/VB friendly
  • Visual Studio
    • Multitargeting for VS2010: 2.0-4.0 + Silverlight
    • Error list improvements
    • Much improved debug display of unions and other F# types.
    • F1 support
  • F# Interactive
    • #load .fsx
  • F# PowerPack
    • Async additions
    • FsHtmlDoc.exe
  • Miscellaneous
    • Deletion of features deprecated in previous F# releases
    • Plus many minor fixes and improvements

F# Language

OverloadID no longer required for defining overridden methods

Overloaded methods can now be defined without the need for the OverloadID attribute.

type Foo() =

    member this.CountChars(s : string ) = s.Length

    member this.CountChars(c : char) = 1

Require module/namespace at top of files

All .fs files, except the last file in an application project, must begin with either a module or namespace declaration. Previously files were put in a default module named after the file. Files can be structured to define an F# module:

module Widgets

type T() =

    member this.X = 12

let x = 12

Or a namespace:

namespace Widgets

type T() =

    member this.X = 12

Nested modules and namespace may be defined as before.

Note: Code of the form

namespace Widgets

module Module1

should be rewritten as

module Widgets.Module1

 ‘comparison’ and ‘equality’ constraints

Two new F#-specific constraints are added:

ty : comparison

ty : equality

Note: Equality includes "hashing"

A type satisfies ty : equality under these conditions

Ø if the type has dependencies T1, ..., TN then each of these must satisfy equality; AND

Ø the type doesn't have the NoEquality attribute; AND

Ø the type is not inferred to be NoEquality (for record, union and struct types)

A type satisfies ty : comparison under these conditions

Ø if the type has dependencies T1, ..., TN then each of these must satisfy comparison; AND

Ø the type doesn't have the NoComparison attribute; AND

Ø the type implements IComparable, or the type is an array/IntPtr/UIntPtr; AND

Ø the type is not inferred to be NoComparison (for record, union and struct types)

One ramification of this is that most .NET types satisfy equality, even if this defaults to reference equality.

These constraints are used on some common parts of the F# library:

    val max : 'T[] -> 'T when 'T : comparison

    val min : 'T[] -> 'T when 'T : comparison

    val sort : 'T[] -> ‘T[] when 'T : comparison

    val sortInPlace: 'T[] -> unit when 'T : comparison

    val maxBy : ('T -> 'Key) -> ‘T[] -> 'T when 'Key : comparison

    val minBy : ('T -> 'Key) -> ‘T[] -> 'T when 'Key : comparison

    val sortBy : ('T -> 'Key) -> ‘T[] -> ‘T[] when 'Key : comparison

    val sortInPlaceBy: ('T -> 'Key) -> ‘T[] -> unit when 'Key : comparison

    val countBy : ('T -> 'Key) -> seq<'T> -> seq<'Key*int> when 'Key : equality

    val distinct : seq<'T> -> seq<'T> when 'T : equality

    val distinctBy : ('T -> 'Key) -> seq<'T> -> seq<'T> when 'Key : equality

    val ( < ) : 'T -> 'T -> bool when 'T : comparison

    val ( > ) : 'T -> 'T -> bool when 'T : comparison

    val ( >= ) : 'T -> 'T -> bool when 'T : comparison

    val ( <= ) : 'T -> 'T -> bool when 'T : comparison

    val ( = ) : 'T -> 'T -> bool when 'T : equality

    val ( <> ) : 'T -> 'T -> bool when 'T : equality

    val compare : 'T -> 'T -> int when 'T : comparison

    val hash : 'T -> int when 'T : equality

    val max : 'T -> 'T -> 'T when 'T : comparison

    val min : 'T -> 'T -> 'T when 'T : comparison

In some cases, code may need to be updated to take into account these constraints.

F# types may indicate how they participate in generic comparison or equality using attributes NoEquality/Comparison, StructuralEquality/Comparison. These are used on some F# library types, for example:

 

   

    [<NoEquality>]

    [<NoComparison>]

    type Async<'T>

In some circumstances it is also useful to make equality and comparison of a collection type conditional on a type parameter having equality/comparison. This is done using the EqualityConditionalOn and ComparisonConditionalOn attributes:

    type Set<[<ComparisonConditionalOn>] 'T when 'T : comparison>

These attributes are inferred for structural types:

    // inferred: [<EqualityConditionalOn>] for T1, T1

    // inferred: [<ComparisonConditionalOn>] for T1, T2

    type Choice<'T1,'T2> =

      | Choice1Of2 of 'T1

      | Choice2Of2 of 'T2

Two new functions are available to access an "Unchecked" implementation of equality and comparison that is equivalent to the regular equality and comparison implementations, as used in previous implementations of F#, but may be used with any types without constraint

 

Unchecked.compare

Unchecked.equals

 

 

Further details of the equality/comparison constraint feature will be contained in the updated F# specification, to be released after these notes are posted.

Core conversion operators use .NET standard op_Implicit/op_Explicit instead or ToInt32, etc.

Overloaded conversion operators such as ‘int’, ‘char’ and ‘float’ which previously provided custom overloading via ToXXX conversion functions now use .NET standard op_Implicit/op_Explicit instead. Any type defining an op_Implicit or op_Explicit returning the expected type can be used with these conversion operators.

type MyInt(x : int) =

    member this.X = x

    static member op_Explicit(myint : MyInt) = myint.X

let myInt = new MyInt(3)

// The 'int' conversion function calls the op_Explicit method on MyInt

let oldInt = int myInt

Support for global namespace qualification, “global.A.B.C” and “namespace global”

Types can now be defined in the .NET “global” namespace using:

namespace global

type T() =

    member this.X = 12

 

Additionally, in cases where partially-qualified names clash, name lookup can be qualified to the global namespace environment:

 

let line = global.System.Console.ReadLine()

 

More flexibility in defining yield! and return! in custom computation expressions

To support more flexible uses of computation expressions, the two computation expression forms yield! and return! are now de-sugared as follows:

yield! expr -> builder.YieldFrom expr

return! expr -> builder.ReturnFrom expr

 

Extension methods can participate in overload resolution

 

Extension methods can be defined as overloads of existing methods. These are given a lower priority in overload resolution. See the F# spec for full details.

type System.String with

    member this.Split(seps : string) =

        this.Split(

            [|seps|],

            System.StringSplitOptions.RemoveEmptyEntries)

       

let parts = "a b c d".Split(" ")

 

Support for letting F# types allow “null” using [<AllowNullLiteral>]

 

Types defined in F# do not generally allow the use of the ‘null’ literal as a proper value. However, null values can be passed in for these types from non-F# .NET callers, and sometimes it is necessary to write F# types that naturally do support a null proper value.

type T1() =

    member this.X = 12

[<AllowNullLiteral>]

type T2() =

    member this.X = 12

let t1 : T1 = null // Error

let t2 : T2 = null // No error

 

Interface implementations can now be inherited

 

Interfaces implemented by inherited classes can be used to satisfy interface implementation requirements on a derived class. Code such as the following now compiles without the need to explicitly implement I1 in C2.

 

type public I1 =

    abstract V1 : string

   

type public I2 =

    inherit I1

abstract V2 : string

type public C1() =

interface I1 with

member this.V1 = "C1"

type public C2() =

inherit C1()

interface I2 with

member this.V2 = "C2"

More flexibility for list literals and subtyping

List and array literals may now be used to create collections from elements that are subtypes of the collection element type. For example:

let myControls: (Control list) = [ new Button() ; new TextBox() ]

F# Compiler

HTML doc generation moved from compiler to PowerPack

The --generatehtml compiler option has been removed, and the functionality moved into a separate tool in the F# PowerPack (see below). This enables decaoupling documentation generation from compilation, and more customization of doc generation.

F# Binary Compatibility 2.0-4.0

As we have discussed with the F# community, full ongoing binary compatibility is expected starting with the version of F# that comes with the Visual Studio 2010 RTM release.

This release has taken crucial steps towards the F# team goal of ensuring binary compatibility for components across .NET 2.0-4.0. For example, in previous releases different copies of the F# Power Pack were required for .NET 2.0-3.5 and 4.0. However, in this release we have one version of the Power Pack which can be used on both .NET 2.0-4.0 (installed via the CTP MSI or ZIP).

If you are authoring F# binary components, you can now build binary compatible components in a similar way. There are some important caveats to this due to two missing type forwarders in FSharp.Core.dll, notably

Ø DLLs using Array2D.* or 2D arrays in public API surface area are not automatically binary compatible across 2.0/4.0. With care these DLLs can be made binary compatible across 2.0/4.0, and this has been done for the F# Power Pack, but normally these APIs will not be binary compatible across 2.0/4.0 until Visual Studio 2010 RTM

Ø Likewise DLLs using F# lazy values (System.Lazy<_>) are not automatically binary compatible. With care, these DLLs can be made binary compatible across 2.0/4.0 by using a private copy of the F# lazy implementation from .NET 2.0, and this has been done for the F# Power Pack. However in the absence of special attention these APIs will not be binary compatible across 2.0/4.0 until Visual Studio 2010 RTM

Binary compatibility can be checked in part by running peverify for .NET 2.0 and .NET 4.0 after installing Visual Studio 2010, a version of Visual Studio 2008 and the F# CTP.

F# Core Libraries

Async API Additions for Web Programming

The extension methods webClient.AsyncDownloadString and webRequest.AsyncGetResponse are now defined in FSharp.Core.dll, and accessed via

open Microsoft.FSharp.Control.WebExtensions

For example:

open System.Windows.Forms

open System.Net

open System.IO

open Microsoft.FSharp.Control.WebExtensions

/// Fetch the contents of a web page, asynchronously

let httpAsync (url:string) =

async { let req = WebRequest.Create(url)

let! resp = req.AsyncGetResponse()

// the rest is a callback

use stream = resp.GetResponseStream()

use reader = new StreamReader(stream)

return reader.ReadToEnd() }

Async API Additions for WinForms, WPF, Silverlight and ASP.NET applications

The Async API has a number of new additions and design extensions to better enable writing “single-primary-thread-of-control” reactive applications using F# asyncs. This is a commonly used idiom for Windows Forms, WPF, Silverlight and ASP.NET programming.

These applications are I/O parallel, but only use CPU parallelism in constrained ways. In this kind of application, CPU processing is done on a single thread of control, but multiple I/O requests may be overlapped.

In .NET, these applications will typically have a non-null, dedicated value for System.Threading.SynchronizationContext.Current on the main “thread”.

Waiting for .NET Events

The method Async.AwaitEvent is now defined in FSharp.Core.dll. This method runs the continuation of the async the first time .NET event fires.

open System.Windows.Forms

let form = new Form(Visible=true,TopMost=true)

async { let! firstClick = Async.AwaitEvent form.Click

form.Text <- "I got clicked once!"

let! nextClick = Async.AwaitEvent form.Click

form.Text <- "I got clicked twice!" }

|> Async.StartImmediate

Starting Asyncs on the Current Thread

The methods Async.StartImmediate and Async.StartWithContinuations are now defined in FSharp.Core.dll. These methods start the async immediately on the current thread, until the first point where the thread yields through an operation such as Async.AwaitEvent or an I/O operation.

These methods are particularly useful for starting async operations on the GUI or ASP.NET thread.

A use of StartImmediate is shown above.

Automatic Return to Context

F# library async I/O and waiting primitives can now be used from GUI or ASP.NET threads in a simpler fashion. Conceptually, if you start an async I/O operation or an async wait operation on a GUI thread, then it will “complete” on the GUI or ASP.NET thread.

If a primitive async operation is started on a thread where SynchronizationContext.Current is set, then the continuation of such an async is posted using SynchronizationContext.Post.

In the following example the overall async is started on the GUI thread using StartImmediate. As the click happens and the asynchronous fetches of the web pages return, the overall async is in each instance continued on the GUI thread. This means the GUI components can be safely updated from the async.

open System.Windows.Forms

let form = new Form(Visible=true,TopMost=true)

async { let! firstClick = Async.AwaitEvent form.Click

form.Text <- "I got clicked!"

let! firstPage = httpAsync "www.google.com"

form.Text <- "I got the first page!"

let! secondPage = httpAsync "www.bing.com"

form.Text <- "I got the second page!" }

|> Async.StartImmediate

F# asyncs built using the following API operations implement “automatic return to context”:

Async.Parallel

Async.Sleep

Async.AwaitEvent

Async.AwaitIAsyncResult

Async.AwaitTask

Async.AwaitWaitHandle

Async.FromBeginEnd

Async.FromContinuations

type System.Net.WebRequest with

member AsyncGetResponse()

type System.Net.WebClient with

member AsyncDownloadString()

type System.IO.Stream with

member AsyncRead()

member AsyncWrite()

Async API Renamings

System.Threading.AsyncSleep -> Async.Sleep

extension waitHandle.AsyncWaitOne -> Async.AwaitWaitHandle

extension waitHandle.AsyncWaitOne -> Async.AwaitWaitHandle

Async.BuildPrimitive -> Async.FromBeginEnd

Async.SwitchToGuiThread -> Async.SwitchToContext

Async.RunWithContinuations -> Async.StartWithContinuations

Complete the under_score -> camelCase name standardization, of_list -> ofList

The naming normalization across the F# core libraries is finalized in this release, with the following removals of underscores and abbreviations:

to_list -> toList

to_array -> toArray

to_seq -> toSeq

of_list -> ofList

of_array -> ofArray

of_seq -> ofSeq

of_nativeint -> ofNativeInt

to_nativeint -> toNativeInt

hd -> head

tl -> tail

Observable module and F# events

The IObservable and IObserver interfaces provide a general interface over observable data, which can be easily composed. These interfaces are provided in .NET4.0, and are available in F# for .NET2.0.

1st class events in F# now implement the IObservable interface, enabling them to interoperate with other IObservable programming models. A new Observable module in the F# library provides compositional functions for manipulating general Observable data:

Observable.merge

Observable.map

Observable.filter

Observable.partition

Observable.split

Observable.choose

Observable.scan

Observable.add

Observable.subscribe

Observable.pairwise

array2D enables easily creating 2D arrays

A new ‘array2D’ function converts data into 2d arrays:

let matrix = array2D [[1;2;3];

[2;3;4];

[3;4;5]]

[.NET 4.0] Lazy, CancellationToken, BigInteger in .NET4.0

Some F# types have moved into mscorlib in .NET4.0, making it easier to reuse these types between F# and other .NET languages. On .NET2.0, these F# types have been moved to corresponding namespaces within FSharp.Core.dll. The following important F# types have moved to System namespaces:

Lazy -> System.Lazy

Tuple -> System.Tuple

BigInt -> System.Numerics.BigInteger

AsyncGroup -> System.Threading.CancellationTokenSource and System.Threading.CancellationToken

CompiledName used to ensure .NET view is C#/VB friendly

The .NET-visible names of types and members in FSharp.Core.dll are modified to align with standard .NET naming conventions. This makes it easier to use FSharp.Core defined types from other .NET languages. For example:

Seq.iteri -> Microsoft.FSharp.Collections.SeqModule.IterateIndexed

failwith -> Microsoft.FSharp.Core.Operators.FailWith

Visual Studio

[VS2010] Multitargeting support for 2.0-4.0 + Silverlight

With VS2010, F# users can target .NET 2.0, 3.0, 3.5, 3.5 Client, 4.0 and 4.0 Client. F# libraries can also be built targeting Silverlight 2 and 3. F# libraries built against .NET2.0 can also be used as part of F# applications targeting later versions of .NET.

Error list improvements

The Visual Studio error list is populated with F# errors both during editing of F# files, and during building of F# projects. This release ensures that errors are tracked between these two sources when they are available in both.

F1 support

F1 contextual help support is available in the F# editor. This will open documentation for the type or member reference at the cursor. In addition, in VS2010, F1 help on F# keywords or F# library functions will go to the new MSDN documentation on these subjects.

F# Interactive

#load .fsx

When building complex F# scripts, it is sometimes necessary to split a script into multiple .fsx files. F# scripts now support the ability to load other F# scripts, enabling factoring of scripts into source components.

F# PowerPack

FsHtmlDoc.exe

A new tool called fshtmldoc.exe is now available in the F# Power Pack. This replaces the HTML doc functionality in the F# compiler. The command line options are shown using “fshtmldoc.exe --help"

Comments

  • Anonymous
    December 08, 2009
    With OverloadID deprecated, how do I overload on return type now? This used to work:    member x.Walk() = 42    [<OverloadID("WalkReturningString")>]    member x.Walk() = "A String"