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
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
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
…
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.
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
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"
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() ]
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.
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.
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”.
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
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
[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.
#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.
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"
- 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"