Detailed Release Notes for the F# September 2008 CTP release

These are the detailed release notes for the F# September 2008 CTP release. The release announcement is here. We will be publishing a known issues list here shortly and augmenting it as new issues are found.

[ Update: The following issues were addressed in the 1.9.6.2 update to the CTP on 6 Sep 2008

    3423 type abbreviations to floating point types involving units don’t behave correctly

    3424 Quotations.Expr.TupleGet gives exception on zero index

    3425 CodeDom invocation of compiler should use '--nologo'

    3427 decimal<kg> doesn't support operators

    3419 Sequence expressions involving 'let' and 'use' dispose of enumerator too eagerly

    3498 Evaluation of a valid quotation expression doesn’t succeed (reported by Credit Suisse)

    3491 base variables in object expressions do not follow the same rules as base variables in class types

 

Summary

  • The F# Visual Studio project system and language service have been completely re-implemented.
  • F# Project Files now use suffix .fsproj and support MSBuild.
  • F# Interactive in Visual Studio has been greatly enhanced as a tool window. The menu item is now under View -> Other Windows
  • Enhanced scripting support
  • The F# language now supports "Units of Measure" checking and inference
  • The command line options for the F# compiler have been simplified and aligned with standard .NET practice, though retain the -- notation.
  • The F# library is split into two components. FSharp.Core.dll: Contains the core F# libraries, which will be stabilized and versioned infrequently. FSharp.PowerPack.dll: Contains additional useful F# libraries and tools which will version more frequently, and allow continued innovation on top of the core F# language and libraries. The fslex and fsyacc tools are part of the F# PowerPack.
  • .NET 1.x is no longer supported. .NET 2.0 or above is needed by this version of the F# compiler and runtime libraries.
  • The F# Visual Studio integration now requires Visual Studio 2008 (and may be used with the freely available Visual Studio 2008 Shell).
  • #light or #light "off" required in .fs, .fsx and .fsi files
  • "AutoOpen" attribute now supported
  • F# functions whose inferred type includes an unsealed type in argument position may now be passed subtypes when called, without the need for explicit upcasts.
  • Sequence and computation expression syntax regularization and simplification
  • Improvements to debugging and stepping (requires tailcalls be explicitly disabled)
  • Simplified event creation and publication
  • Simplified Reflection API
  • Simplified quotation language feature and API
  • Namespace abbreviations now deprecated
  • Language improvements for better software engineering practice
  • F# now respects COM automation optional and default parameters
  • Operator resolution defaults to resolving to operators defined as static members in relevant argument types if no other definition of the operator is given
  • PowerPack support for quotation compilation and evaluation via LINQ expression trees
  • The PowerPack lexer generator tool 'fslex' now supports Unicode inputs
  • PowerPack support for query evaluation via LINQ expression Trees
  • Deletion of features deprecated in previous F# releases

Tool Improvements

The F# Visual Studio Project System and Language Service have been completely re-implemented.

F# Project Files now use suffix .fsproj and support MSBuild.

A converter for the old project file format is available on the F# team blogs.

Enhanced Scripting Support

Script files .fsx are now governed by slightly different rules to .fs files.

  • #r references are only supported in .fsx files. For .fs files use a command line compilation reference or project support in Visual Studio.
  • On Windows, #r references have been enhanced and resolve to assemblies registered in one of the directories indicating installed .NET software under the standard AssemblyFoldersEx registry key.
  • In the Visual Studio project system, scripts are given special status. They are no longer included in the build by default (though you may set a property to include them), and must include explicit #r references for their dependent DLLs.
  • Intellisense and interactive type checking now respect the #load command
  • In .fs files, warnings are given if one of your top-level expressions has a result that is no explicitly discarded (i.e. a non-unit type)
  • The #use command has been removed, though a startup script may still be specified when using F# Interactive

Compiler command line option normalization

The command line options for the F# compiler have been simplified and aligned with standard .NET practice, though retain the -- notation. Here are a few notable changes:

  • --generate-interface-file has been renamed to --sig
  • --no-warn has been renamed to --nowarn (a number of other options similarly have hyphens removed)
  • Optimization flags have changed; instead of -O3, now use --optimize or just -O

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

Language Enhancements and Updates

Units of Measure Inference and Checking

The F# CTP sees the first release of units of measure checking and inference, an F# language feature with potential to greatly improve the productivity of scientists, engineers, finance and data analysts who routinely work with floating point numbers. The F# type system now makes it possible to annotate values involving floating point numbers with the units they are measured in.. F# type-checking will then check that calculations correctly preserve units, and type inference will infer a unit-annotated type for code if unit annotations are introduced.

A full introduction to units of measure will be posted as a blog series at or around the time of this release on msdn.microsoft.com/fsharp. Some short examples are shown below.

A unit of measure representing kilograms

     [<Measure>] 
    type kg

A unit of measure representing seconds

     [<Measure>] 
    type s

A constant value in kilograms

     let x = 3.0<kg>

A constant value in seconds:

     let y = 2.5<s>   // y : float<s>

Computing a value of type float<kg/s>

     let z = x / y    // z : float<kg/s>

Trying to add a value of type float<kg> to a value of type float<s>. This gives: Error: "The unit of measure 's' does not match the unit of measure 'kg'"

     let w = x + y

#light or #light "off" required in .fs, .fsx and .fsi files

F# code files must now begin with either #light or #light "off" – indicating whether they will use the lightweight F# syntax or not. The most common mode of usage for F# is to use #light. Note: this rule is only enforced by a warning in this version of F#.

Files with extension .ml and .mli do not need the annotation and are implicitly #light "off" .

Subsumption

F# functions whose inferred type includes an unsealed type in argument position may now be passed subtypes when called, without the need for explicit upcasts. For example:

     type Base() = 

        member b.X = 1

    type Derived(i : int) =

        inherit Base()

        member d.Y = i

    let d = new Derived(7)

    let f (b : Base) = b.X

    // Call f: Base -> int with an instance of type Derived

    let res = f d    

    // Use f as a first-class function value of type : Derived -> int 

    let res2 = (f : D -> int)

This aligns members and functions to both do automatic upcasts at calls. This also means that #-types are needed less often. For generic functions, this only applies if the uninstantiated function type contains unsealed types in 'argument position', i.e. as part of a tuple in the curried arguments to a function. Technically speaking, F# supports subsumption by introducing type flexiblity at each point a function value is referred to based on the uninstantiated inferred type of the function.

This change may in some cases, change the inferred types of your functions, though that will normally only be noticeable if you are using signature files.

Automatic upcasting (subsumption) now also applies to a few other language constructs:

  • Record construction
          type Rec = { A : Base ; X : int }

         let r = { A = new Derived(7); X = 12}
  • Union case construction
          type Uni = A of Base | X of int

         let u = A(new Derived(7))
  • Mutable field assignment
          type Rec2 = { mutable A : Base ; X : int }

         let r2 = { A = new Derived(7); X = 12}

         r2.A <- new Derived(3)

This change does not change the rules for subsumption on:

  • tuple creation
  • list elements
  • function binding and "return types"
  • if/then/else

Language improvements for better software engineering practice

  • #nowarn is now scoped to the end of a file.

     

  • Namespaces should be opened with a fully-qualified path. A namespace open directive should be provided a fully-qualified namespace path. Thus code like the following now gives a warning:

     

         open System
        open Windows // warning now given
        open Forms
        // open System.Windows.Forms // preferred way

Namespace abbreviations no longer supported

It is no longer possible to use the module abbreviation syntax to define an abbreviation for a namespace:

      module WF = System.Windows.Forms

This is because the element on the right is not a true module.

Regular rules for "base"

F# now applies the same rules regarding “base” variables to both object expressions and types, so that code such as

 

let obj = { new System.Object() as baseObj with

               member x.ToString() = "I'm an object: " + baseObj.ToString() }

 

will generate a deprecation error and should be replaced by

 

let obj = { new System.Object() with

               member x.ToString() = "I'm an object: " + base.ToString() }

Signatures for types with hidden representation must use [<Sealed>]

In signatures, it is now necessary to use = and [<Sealed>] for types that have methods but whose representation is hidden, e.g.

      [<Sealed>]
     type PositionWithMessage =

             member Position : Lexing.position

             member Message : string

instead of

      type PositionWithMessage with

             member Position : Lexing.position

             member Message : string

"assert(false)" is no longer given a generic type.

In previous versions of F#, an 'OCaml' rule was implemented giving assert(false) a generic type, allowing it to be used as an expression of any type, raising an exception when executed. However, in F# 1.9.4, a change was made to give assert a standard .NET meaning where it becomes a conditional call to System.Diagnostics.Debug.Assert, This means expressions such as assert (1=2) will not fire in your code unless --define DEBUG is specified. This follows standard .NET software engineering practice.

This release removes the 'OCaml' rule for assert(false) since raising an exception for an assertion in non-debug code does not conform to .NET practice.

"AutoOpen" attribute now supported

Modules may be marked with the AutoOpenAttribute, indicating they are implicitly opened if their enclosing namespace or module is opened.

Assemblies may be given one or more AutoOpenAttributes taking strings, indicating the given namespaces or modules should be opened implicitly if the assembly is referenced.

F# now respects COM automation optional and default parameters

The F# syntax for named and optional arguments may now be used in conjunction with COM automation, and F# code generation respects COM automation defaults for omitted arguments. For example:

     chartobject.Chart.ChartWizard(Source = range5,
                                  Gallery = XlChartType .xl3DColumn,
                                  PlotBy = XlRowCol.xlRows,
                                  HasLegend = true,
                                  Title = "Sample Chart",
                                  CategoryTitle = "Sample Category Type",
                                  ValueTitle = "Sample Value Type")

Previously this call required numerous additional "missing value" arguments.

Simpler operator definition and resolution

If no other definition of an operator is given, then an operator resolves to a type-directed "member constraint" operator in much the same way as + and * . For example

         type Receiver(latestMessage:string) =
            static member (<--) (receiver:Receiver,message:string) = 
                Receiver(message)

            static member (-->) (message,receiver:Receiver) = 
                Receiver(message)

        let r = Receiver "no message"

        r <-- "Message One" // The 'default' definition of the operator associates it with a static 
                            // member through the use of a member constraint

        "Message Two" --> r

Slicing operators

It is now easier to extend F# slicing operators to user-defined types. The slicing operators are compiled as calls to a general set of Get/SetSlice methods. For 1D slices:

     e1.[e2opt.. e3opt]        --> e1.GetSlice(arg2,arg3)

    e1.[*]                    --> e1.GetSlice(None,None)

where argi is Some(eiopt) if eiopt is present and None otherwise.

Sequence and Computation Expression Syntax Regularization and Simplification

The syntax for sequence and computation expressions has been simplified. There are no functional changes, but some forms of syntax have been deprecated or removed in favor of a more consistent and regular subset of the syntax.

  • Imperative actions in computation expressions no longer need to be prefixed by do. Like in the rest of the F# language, “do” is no longer required for imperative actions in computation expressions:
     let s = 
        seq { for i in 1..12 do
                  printfn "i = %A" i
                  yield i+2 }
  • The full range of ‘let’ bindings are permitted in computation expressions. Let bindings in computation expressions now have the same syntax as in the rest of the F# langauge:
     let s2 = 
        async { let rec f x = f x + 1
                return 1 }
  • -> and ->> are now deprecated except in the compact sequence expressions. The two shorthand’s -> and ->> are now deprecated except in the sequence expression short form: seq { for x in c -> f(x) } . In all other sequence expressions, use the full names yield and yield! .

Inlined Functions now have Generated Code

In previous versions of F#, function's marked inline did not have code generated. These functions now get code to allow them to be invoked dynamically using .NET reflection.

In some rare cases inlined functions are implemented using unverifiable code. If necessary you can add the NoDynamicInvocationAttribute to a function to disable the generation of code, in which case the function will raise an exception if dynamically invoked.

Improvements to debugging and stepping, though requires tailcalls to be explicitly disabled

Debugging and stepping in Visual Studio and other .NET debuggers has been improved. However, reliable stepping is only available if tailcalls are explicitly disabled through an appropriate command line compiler flag (e.g. --optimize- notailcalls).

Library Changes

Simplified Reflection API

The Microsoft.FSharp.Reflection library has been refactored to leverage .NET reflection concepts more broadly.

Simplified Quotations and Quotation API

The Microsoft.FSharp.Quoataions namespace has been refactored to enable provide a more consistent API

  • The namespaces and modules are now:
         Microsoft.FSharp.Quotations
        Microsoft.FSharp.Quotations.Patterns
        Microsoft.FSharp.Quotations.DerivedPatterns
        Microsoft.FSharp.Quotations.ExprShape
  • The modules Microsoft.FSharp.Quotations.Raw and Microsoft.FSharp.Quotations.Typed have been removed
  • Expr<_> is now a subtype of Expr.
  • Splicing of values now "comes for free", i.e.
                 let f (x:int) = <@ 3 + x @>

constructs a quotation with the value of x substituted at the given place-holder.

  • Splicing of expressions now uses %expr, so
                 let f (expr:Expr) = <@ 3 + %expr @>

constructs a quotation with the value of the expression tree expr is substituted at the given place-holder.

  • The primitive constructors have been renamed. “Mk” has been dropped throughout. The full set of primitive constructors is:
     Expr.AddressOf : Expr -> Expr
    Expr.AddressSet : Expr * Expr -> Expr
    Expr.Application: Expr * Expr -> Expr
    Expr.Call : MethodInfo * list<Expr> -> Expr
    Expr.Call : Expr * MethodInfo * list<Expr> -> Expr
    Expr.Coerce : Expr * Type -> Expr 
    Expr.IfThenElse : Expr * Expr * Expr -> Expr 
    Expr.ForIntegerRangeLoop: Var * Expr * Expr * Expr -> Expr 
    Expr.FieldGet: FieldInfo -> Expr 
    Expr.FieldGet: obj:Expr * FieldInfo -> Expr 
    Expr.FieldSet: FieldInfo * value:Expr -> Expr 
    Expr.FieldSet: obj:Expr * FieldInfo * value:Expr -> Expr 
    Expr.Lambda : Var * Expr -> Exp
    Expr.Let : Var * Expr * Expr -> Expr
    Expr.LetRec : (Var * Expr) list * Expr -> Expr
    Expr.NewObject: ConstructorInfo * Expr list -> Expr
    Expr.DefaultValue: Type -> Expr
    Expr.NewTuple: Expr list -> Expr
    Expr.NewRecord: Type * Expr list -> Expr 
    Expr.NewArray: Type * Expr list -> Expr 
    Expr.NewDelegate: Type * Var list * Expr -> Expr 
    Expr.NewUnionCase: UnionCaseInfo * Expr list -> Expr 
    Expr.PropGet:  Expr * PropertyInfo  * ?indexerArgs: Expr list -> Expr 
    Expr.PropGet: PropertyInfo * ?indexerArgs: Expr list -> Expr 
    Expr.PropSet: Expr * PropertyInfo * Expr * ?indexerArgs: Expr list -> Expr 
    Expr.PropSet: PropertyInfo * Expr * ?indexerArgs: Expr list -> Expr 
    Expr.Quote: Expr -> Expr 
    Expr.Sequential: Expr * Expr -> Expr 
    Expr.TryWith: Expr * filterVar:Var * filterBody:Expr * catchVar:Var * catchBody:Expr -> Expr 
    Expr.TryFinally: Expr * Expr -> Expr 
    Expr.TupleGet: Expr * int -> Expr 
    Expr.TypeTest: Expr * Type -> Expr 
    Expr.UnionCaseTest: Expr * UnionCaseInfo -> Expr 
    Expr.Value : obj * Type -> Expr
    Expr.Value : 'a -> Expr
    Expr.Var : Var -> Exp
    Expr.VarSet : Var * Expr -> Expr
    Expr.WhileLoop : Expr * Expr -> Expr

The primary active patterns for working with expressions are in Microsoft.FSharp.Quotations.Patterns and align with these

Some specific “helper” patterns are contained in Microsoft.FSharp.Quotations.DerivedPatterns. These roughly match those in F# 1.9.4 as follows:

           GenericTopDefnApp --> DerivedPatterns.SpecificCall
          ResolvedTopDefnUse(info,body) --> 
                  DerivedPatterns.MethodWithReflectedDefinition(minfo); OR
                  DerivedPatterns.PropertyGetterWithReflectedDefinition(minfo)

The BindingPattern has been renamed to ExprShape and its constituent members renamed and simplified.

The IEvent module is renamed to Event, with the old module still present though deprecated. Apart from that, the existing F# event model of creating a first class event using Event.create is supported unchanged. However, some regularization and simplification has been made for creating events whose handlers have a specific delegate type:

  • You may create regular events using new Event<args>() and events for a specific delegate type using new Event<delegate,args>() . You should think of these types as "event implementations"
  • You may trigger events with ev.Trigger(args) and ev.Trigger(sender,args)
  • You may publish events with ev.Publish
  • The type IHandlerEvent<_> has been deleted

For example:

     type MyControl() =
        let tickEvent = new Event<int>()
        member self.React() = 
            tickEvent.Trigger (4)
        member self.OnTick = tickEvent.Publish 

Deletion of features deprecated in previous F# releases

  • All Unicode symbols deleted from the F# syntax
  • IEnumerable.* deleted
  • List.of_IEnumerable, List.to_IEnumerable, now deleted in favour of List.of_seq, List.to_seq
  • Permutation type deleted, with permutations now represented by functions. The Permutation module remains in the PowerPack
  • Func.*, e.g. Func.repeatN deleted
  • CompatArray and CompatMatrix modules deleted. Use Array and Array2 instead
  • Microsoft.FSharp.Compatibility.CompatMatrix -- .NET 1.x only
  • Microsoft.FSharp.Compatibility.CompatArray -- .NET 1.x only
  • Microsoft.FSharp.Core.Idioms deleted
  • Microsoft.FSharp.Core.Int8 deleted
  • Microsoft.FSharp.Core.Int16 deleted
  • string is now a function of type 'a -> string. Thus string.Format now gives compilation error, use System.String.Format instead. Likewise string.Empty.
  • base is now used as a keyword and no longer gives a warning
  • The type of truncate is now 'a -> 'a (indeed for any floating point type or a type supporting a static Truncate method)
  • (type ty) is now typeof<ty> and typedefof<ty>
  • Abstract types must be marked with [<AbstractClass>]
  • Declarations that may be interpreted as either functions or patterns now interpreted as functions, e.g.
                let Node(x,l1,r1) = idxToNode(m1) // now always a function definition 

The F# PowerPack

The F# PowerPack is a collection of value add and compatibility components for use with F#. The plan of record is that these will not be part of the supported components that make up a release of F#, but will rather become a shared-source project on codeplex. You are also encouraged to use the PowerPack code as a guide and a sample should you need to change or extend the functionality implemented there.

As a result the F# PowerPack contains a mixture of components:

  • Components for compatibility with OCaml and/or earlier release of F#
  • Math components including the Matrix and Vector types
  • A component giving the definitions of SI Units of Measure
  • Some deprecated components
  • Components for supporting evaluation of F# quotations via LINQ and the execution of quotations as LINQ queries

If you wish to program without a dependency on the PowerPack, then some changes may be necessary. Most are marked by warnings from the compiler. For example:

       Sys.argv --> System.Environment.GetCommandLineArgs() 

      Char.code --> int

      Char.chr --> char

      Filename.* --> Use System.IO.Path methods instead

      Bytearray --> Array

PowerPack support for Quotation Compilation and Evaluation via LINQ Expression Trees

Note: this PowerPack feature is marked experimental in this release

The assembly FSharp.PowerPack.Linq.dll contains support for the translating F# quotation expressions to LINQ expression trees. Since LINQ expression trees can be dynamically compiled this allows a form of compilation end execution for F# quotation expressions. For example:

    open Microsoft.FSharp.Linq.QuotationEvaluation
   
   let q = <@ 1 + 1 @>
   
   q.ToLinqExpression () // the corresponding LINQ expression is then shown 
   
   q.Compile () // a value of type 'unit -> int' is returned 

   q.Eval () // the value 2 is returned 

Note: The performance goal of quotation execution via LINQ is not to match that of compiled F# code: performance can be 5X slower or more. In particular, the LINQ dynamic expression compiler has limitations that require workarounds and overhead in the Quotation-to-LINQ expression tree translator. Instead, the purpose of the translator is to provide a generic way of dynamically evaluating leaf fragments of quotations while the 'heavy lifting' is performed by other core routines. A classic use of this kind of mechanism is in the implementation of portions of a query evaluator, where the core query is executed on a relational database and additional, cheap post-processing is performed on the client.

Note: Some size limitations exist in the implementation of this component. In particular, closures may not contain more than 20 free variables, and mutually recursive function groups may involve at most 8 mutually recursive functions.

PowerPack support for Query Evaluation via LINQ Expression Trees

Note: this PowerPack feature is marked experimental in this release

The assembly FSharp.PowerPack.Linq.dll contains support for executing F# quotation expressions as queries under the LINQ IQueryable paradigm. For example:

     open Microsoft.FSharp.Linq.Query

    type Customer = { Name:string; Data: int; Cost:float; Sizes: int list }
    let c1 = { Name="Don"; Data=6; Cost=6.2; Sizes=[1;2;3;4] }
    let c2 = { Name="Peter"; Data=7; Cost=4.2; Sizes=[10;20;30;40] }
    let c3 = { Name="Freddy"; Data=8; Cost=9.2; Sizes=[11;12;13;14] }
    let c4 = { Name="Freddi"; Data=8; Cost=1.0; Sizes=[21;22;23;24] }

    let data = [c1;c2;c3;c4]
    let db = System.Linq.Queryable.AsQueryable<Customer>(data |> List.to_seq)

    // Now execute a query
    <@ seq { for i in db do 
               for j in db do 
                 yield (i.Name,j.Name) } @> 

Here a query is an expression of a particular form involving manipulations on F# sequences. The form of sequences accepted is shown below:

     query <@ qexpr-with-post-processing @>

    qexpr-with-post-processing =
      | qexpr
      | [ comp-qexpr ]
      | [| comp-qexpr |]
      | qexpr |> Seq.length
      | qexpr |> Seq.hd 
      | qexpr |> Seq.find select-expr
      | qexpr |> Seq.max
      | qexpr |> Seq.min
      | qexpr |> Seq.average 
      | qexpr |> Seq.average_by select-expr 
      | qexpr |> Seq.sum 
      | qexpr |> Seq.sum _by select-expr 
      | qexpr-with-post-processing |> Query.max_by (fun v -> select-expr[v])
      | qexpr-with-post-processing |> Query.min_by (fun v -> select-expr[v])
                        for aggregation types float, float32, decimal 
      | macro
          reflected-definition 
          reflected-definition args
          let v = e in qexpr-with-post-processing
          let f args = e in qexpr-with-post-processing

      | also prefix application versions of piped syntax


    qexpr = 
      | expr: IQueryable<_>                  // db.Customers, f x
      | seq { comp-qexpr }
      | if expr then qexpr1 else qexpr2
      | match expr with qpati -> qexpri 
      | Seq.empty 
      | qexpr |> Seq.map_concat (fun v -> qexpr) 
      | qexpr |> Seq.filter select-expr 
      | qexpr |> Seq.map select-expr 
      | qexpr |> Seq.take expr 
      | qexpr |> Seq.sort              
      | qexpr |> Seq.distinct
      | qexpr |> Seq.sort_by select-expr 
      | qexpr |> Seq.delay qexpr
      | qexpr |> Seq.exists   
      | qexpr |> Seq.for_all  
      | Seq.append qexpr1 else qexpr2

      | qexpr |> Query.group_by select-expr 
      | qexpr |> Query.contains expr 
      | Query.join qexpr1 qexpr2 select-expr select-expr select-expr
      | Query.group_join qexpr1 qexpr2 select-expr select-expr select-expr

      | macro
      | also prefix application versions of piped syntax

    comp-qexpr =
      | for qpat in qexpr do comp-qexpr 
          // Note: only simple variable patterns should be used
      | yield expr      

The semantics of query execution are given by translation to an LINQ expression involving uses of the IQueryable type and executing that expression. This in turn will depend on the query provider implementation.