Welcome to MSDN Blogs Sign in | Join | Help

Detailed Release Notes for F# 1.9.4

These are the full release notes for F# 1.9.4. The download link is on the main announcement page. We'll also use this page to note known issues with the release as we find them.

 

Summary

 

Language

  • Symmetric Operator Overloading
  • Additional nullness checking
  • 'base' variables must now be called 'base'.
  • Intrinsic members take predence over extension members
  • Generic Equality now uses Object.Equals
  • Downcasting For Interface Types
  • Handle Optional Aruments Correctly when using a method as a first class function value
  • Change .[] overload resolution behaviour
  • Deprecate the Unicode symbols in the language
  • Sealed attribute
  • AbstractClass attribute
  • typeof may be used in attributes, deprecate (type ...) syntax
  • Decimal support
  • Supply Named and Optional Aruments to COM methods
  • Allow construction of delegates taking byref arguments

Library

  • fold_left performance improvements
  • Not_found now maps to KeyNotFoundException not IndexOutOfRangeException.
  • Addition of the 'enum' overloaded function.
  • Deprecate the Enum.to_int and Enum.of_int functions.
  • Access control checking for modules and types implemented
  • Deprecate the use of string.CompareTo in favour of System.String.CompareTo
  • Deprecate the IEnumerable module in favour of Seq
  • Deprecate the CompatArray and CompatMatrix modules when used with .NET 2.0
  • Deprecate the truncate OCaml-compatibility function
  • Make 'lock' function safer: may now only take reference types

Tools

  • Counter Examples from Incomplete Pattern Matches
  • Implement redundancy checking for isinst patterns
  • Many Improved Error Messages
  • Ctrl-C now copies, Ctrl-. used for Interrupt in F# Interactive in Visual Studio.
  • Right-click menus supported in F# Interactive in Visual Studio

Current Known Issues

  • Two overloads on "Complex" were deprecated (float * Complex and Complex * float) but no replacements provided. Just ignore the deprecation warning for now.
  • Named parameters with custom attributes give a spurious warning. This may be ignored.
  • There is a known problem using F# in Visual Studio on Vista machines where the Tablet PC input services are running, e.g. laptops. This may be addressed by stopping those services from the Control Panel.

Language Enhancements: Design Extensions and Changes

 

Symmetric Operator Overloading

 

F# uses a form of type-directed operator overloading. (Type-directed operator overloading means that operators such as '+' resolve to different implementations depending on the static types of the two arguments.) Previous releases of F# used asymmetric, type-directed overloading that placed more emphasis on the type of the left argument rather than the type of the right argument. For some time it has been recognized that this form of operator overloading is somewhat unintuitive. This release switches this to use symmetric operator overloading. For example, consider the following cases:

    let f1 x y = x + y              //: int -> int -> int

    let f2 (x:float) y  = x + y     //: float -> float -> float

    let f3 x (y:float)  = x + y     //: float -> float -> float

    let f4 (x:matrix) y = x + y     //: matrix -> matrix -> matrix

    let f5 x (y:matrix) = x + y     //: matrix -> matrix -> matrix

    let f6 (x:matrix) (y:vector) = x * y    //: matrix -> vector -> vector

 

These indicate that operator overloading resolves independent of whether the known type information is associated with the left-hand or right-hand argument of the operator.

 

The two principles that now form the basis of F# operator overloading are

  • symmetry for binary operators and
  • operator overload constraints are resolved using method overload resolution.

Resolution of an operator such as + is performed against a set of potential overload resolutions derived from the left and right hand argument types. For example, when resolving a constraint

 

    static member (+) : float * matrix -> ?

 

The relevant operator overloads are drawn from the float and matrix types. At some points in inference (indeed very frequently) we have incomplete information about the types involved, e.g.

 

    static member (+) : float * ? -> ?

 

In these cases it is important to note that later type information may discover additional nominal information, e.g. to resolve the constraint to

 

    static member (+) : float * matrix -> matrix

 

In more detail,

  • "Strongly determined" resolution is applied as soon as the relevant set of potential overloads becomes closed. In this case, the available operator resolutions are collected from each of the nominal types (e.g. op_Addition static members) and standard method overload resolution rules are applied. For example, if we write OP(?,?,?) for an operator overload constraint, then the constraint OP(float,float,?) resolves immediately to OP(float,float,float).
  • "Weakly determined" resolution is applied at specific points during inference in order to normalize the inference problem prior to making choices about generalization and type-directed overloading. For example, weakly determined resolution is used to resolve the operator constraint OP(Matrix<?>,?,?) that arises from the following code:

    open Microsoft.FSharp.Math

    let f7 (x:Matrix<_>) y = x + y

  • In particular, we apply "Weakly determined" to all constraints involved in
    • generalization
    • method overload resolution for expressions
    • resolving the '.' notation
    • applying defaults at the end of the inference scope


Weakly-determined resolution resolves a constraint if either l.h.s. or r.h.s are nominal types. For example, given
OP(float,?,?) then non-conservative resolution resolves immediately to OP(float,float,float)

 

Conditional Method Calls and assert

 

Conditional method calls are a .NET feature where a call to particular methods marked with System.Diagnostics.ConditionalAttribute are only executed if a conditional compilation symbol such as DEBUG is defined. One common example of this is System.Diagnostics.Debug.Assert. F# now supports conditional method calls.

 

As a result of this change, calls to System.Diagnostics.Debug.Assert may no longer "trigger" in your code.

 

Uses of the assert function are now treated as if they are calls 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.

 

One exception is made for the syntactic expression form assert(false). This is given a special type in F# and OCaml, i.e. has variable return type not unit. This allows it to be used as a "cannot happen" signifier. In this case, if --define DEBUG is not specified then an AssertionFailure exception is raised in order to halt execution. If --define DEBUG is specified then System.Diagnostics.Debug.Assert, and if the assertion is ignored and execution continues then the AssertionFailure exception is raised in order to halt execution.

 

Additional nullness checking

 

.NET is an object-oriented platform and thus includes the notion of null values for reference types. However, the use of null values is, in general, discouraged in F# programming. While it is possible to create null values for reference types, one of the design aims of F# is that this is not a commonly used technique and that, when used, the programmer is made aware of this.

 

In previous release of F#, creating null values of F# types was relatively easily, e.g. through the commonly used unbox function. This technique, however, will now raise a NullReferenceException. For example, the following code will now raise a NullReferenceException:

    type MyRecord = { f : int }

    let x = unbox<MyRecord>(null:obj)

 

This implemented via the use of efficient helper functions related to the types involved.

 

This change also eliminates several somewhat subtle inconsistencies with regard to types that use null as a valid representation. For example, the option type uses null to represent the None option. Unboxing a null value to one of these types now correctly succeeds and returns a valid value of the type.

 

In more detail, there are four kinds of types for F#:

  • Types that have null as an "extra" value, e.g. string and where the use of the null literal is directly permitted, e.g. .NET reference types such as string.
  • Types where the use of null is not directly permitted and which don't use null as a representation, e.g. F# list, record, union, tuple, function, class and interface types
  • Types where the use of null is not directly permitted but which do use null as a representation, e.g. F# unit and option types. Note the null literal may not be used with these types.
  • Value types, where there is no 'null' but do have a 'default' value.

The behaviour of the unbox and type test primitives of F# now treat these different categories appropriately.

 

Note that null is still used as a representation for some F# values, in particular None option values. In this case, the value doesn't carry runtime type information in the manner of other object values, and so type tests against such a value are inherently imprecise. This is correct and expected behaviour for these types.

 

If necessary, null and default values may be generated by calling the new library function Unchecked.defaultof<type>, a helper function specifically designed to make uses of arbitrary null and default values for F# types more traceable in code.

    type MyRecord = { f : int }

    let x = Unchecked.defaultof<MyRecord>

 

Minus syntax

 

Expressions of the form expr -expr, e.g. x -y now give a deprecation warning asking you to rewrite these as either expr - expr or expr-expr. This is to make room for a future design change.

 

New reserved keyword

 

The keyword global has been reserved for future use by F# code

 

Smaller Design Changes

 

  • 'base' variables must now be called 'base'.

A deprecation warning is now given if this is not the case. In a future release of F# base variables will necessarily be called 'base'.

 

  • Intrinsic members take predence over extension members .

Previously extension members were taking priority over intrinsic members in base classes. This has now been corrected.

  • Generic Equality now uses Object.Equals

Uses of the generic equality operator (=) and related operators are now implemented via calls to System.Object.Equals.

  • Downcasting For Interface Types.

The conversion operator expr :?> type can now be used to convert an interface or non-sealed class type to any other interface type. This matches the C# behaviour of this operator, with the exception that conversions from variable types are not permitted: in that case the expression should first be converted to an object using box.

  • Handle Optional Arguments Correctly when using a method as a first class function value.

As has been reported several times in bug reports, when operations such as Async.Run which take optional arguments are used as first-class values the resulting function value previously required the optional argument be made explicit. This has now been changed, and the optional argument is dropped instead.

  • Change .[] overload resolution behaviour.

Previously, the resolution of the expr.[expr] operator did not apply operator overloading rules. This led to incompletenessed where the operator could not be used in conjunction with certain C# types. This has now been corrected.

  • Deprecate the Unicode symbols in the language.

The Unicode symbols for quotations have been deprecated. Instead, the ASCII symbols <@ @> and the prefix operators % and %% should be used instead.

 

Language Feature Completions

 

  • Sealed attribute . This attribute may be added to class types to mark them 'sealed', i.e. no base types.

    [<Sealed>]

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

        member this.X = x

        member this.Y = y

        member this.Sum = x+y

  • AbstractClass attribute . This attribute may be added to class types to mark them 'abstract', i.e. missing implementations of some members, and/or with constructors that can't be used directly

    [<AbstractClass>]

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

        abstract X : int

        abstract Y : int

        abstract GetSum : int -> int

  • typeof may be used in attributes, deprecate (type ...) syntax

    [<SomeAttribute(typeof<int>)>]

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

        abstract X : int

        abstract Y : int

        abstract GetSum : int -> int

  • Decimal literals now implemented. For example, 120.00M. The decimal conversion function is now also supported amongst the standard set of conversion functions, and decimal is no longer a keyword.

    let pocketMoney = 0.25M

    let GDP = 12416505085952.00M

  • Supply Named and Optional Aruments to COM methods. It is now possible to use the F# optional argument mechanism to supply arguments to COM methods. This is very important when working with Excel and Word Primary Interop Assemblies. 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")

  

Here 4 arguments have been omitted from the programmatic specification of an Excel chart.

  • Allow construction of delegates taking byref arguments

Library Enhancements

 

  • fold_left performance improvements.

A number of library functions that take binary functions have been optimized. Many thanks to the folk at Morgan Stanley for sugesting these!

  • Collections that previously threw IndexOutOfRangeException now throw KeyNotFoundException.

The OCaml-compatibility exception type Not_found is now mapped to System.Collections.Generic.KeyNotFoundException, rather than System.IndexOutOfRangeException