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. This means a number of F# functions now raise KeyNotFoundException instead of IndexOutOfRangeException. In the rare case where you explicitly catch IndexOutOfRangeException in your code you will get a warning and may need to adjust your exception handling accordingly. This change was made because IndexOutOfRangeException is a CLR-reserved exception which should not be raised in user or library code.

  • Addition of the 'enum' overloaded function.

The enum function can be used to convert integers to enumerations. Likewise, the function int32 can be used to convert back to an integer.

    let sunday = System.DayOfWeek.Sunday

    let monday = enum<System.DayOfWeek> 1

    let tuesday = enum<System.DayOfWeek> 2

  • Deprecate the Enum.to_int and Enum.of_int functions.

These can be replaced by uses of enum and int32 respectively.

  • Access control checking for modules and types implemented.

That is, private and internal annotations on modules and types are now implemented. Previously these gave a warning.

  • Deprecate the use of string.CompareTo in favour of System.String.CompareTo.

This makes room for a planned design change to add a conversion function called string.

  • Deprecate the IEnumerable module in favour of Seq.
  • Rename MailboxProcessor.PostSync to MailboxProcessor.PostAndReply , deprecating the old name
  • Deprecate the CompatArray and CompatMatrix modules when used with .NET 2.0.

Future releases of F# will make .NET 2.0 the default. These modules are only required when using .NET 1.x. When using .NET 2.0 and above, uses of these modules can be replaced by uses of Array and Array2 respectively.

  • Deprecate the truncate OCaml-compatibility function.

Instead you should use the synonymous for int_of_float or the F# conversion function int, applied to float values. The design of other F# conversion functions and operators means it is much more natural for truncate be an overloaded function applying to a range of numeric types. Thus a deprecation warning is now given on the use of the existing truncate function.

  • Make 'lock' function safer: may now only take reference types.

Tool Enhancements

 

  • Counter Examples from Incomplete Pattern Matches. For example,

    let f x =

        match x with

        | 1 -> 7

        | 2 -> 49

    stdin(12,14): warning FS0025: Incomplete pattern matches on this expression. The value '3' will not be matched

Note: There is actually one known bug here, where incomplete matches on union types do not always give a valid counter example. This is being addressed.

  • Implement redundancy checking for isinst patterns. That is, duplicate or redundant type tests in patterns will now be reported as warnigns.
  • Many Improved Error Messages

 

Enhancements to F# Interactive in Visual Studio

  • Ctrl-C now copies, Ctrl-. used for Interrupt. When used in Visual Studio, it is natural for Ctrl-C to be interpreted as 'copy'. This Ctrl-. ('Control-Stop') is now used for interrupt.
  • Menus supported. The interrupt, select-all, copy, paste and clear options are now available from the right-click menu.

Bugs fixed

 

1171 F# Compiler bug binding to a SOAP dll

1109 F# Compiler Make assert a function

1325 F# Compiler Enums of chars are not accepted by F# Interactive

1316 F# Compiler Throw away invalid paths (#r) in FSI

1341 F# Compiler Compiler should reject explicit class construction when an implicit one is defined

1423 F# Compiler bad error message: The member or object constructor 'Random' takes 1 arguments but is here supplied with 1.

1227 F# Compiler ensure we can supply named and optional arguments to COM-interop methods

1404 F# Compiler optional arguments handled incorrectly when using a method as a first class function value.

1418 F# Compiler Methods on Enums should not be allowed

1457 F# Compiler FParsec test 0.4.2. failing with byref check

1185 F# Compiler change .[] overload resolution behaviour

1244 F# Compiler unnecessarily choosing defaults for variables unconstrained, leading to spurious warnings and true errors being needlessly downgraded to warnings

1430 F# Compiler Compiler spits out duplicate errors when given bogus syntax for named arguments

1462 F# Compiler Poor (and repeated) error message on type mismatch

1034 F# Compiler Downcasting for interface types

151 F# Compiler Cannot construt delegate values that accept byref arguments

1024 F# Compiler bug in type inference allows normal values to be polymorphic (type functions have to be declared explicitly and only at the top level)

1187 F# Compiler problem with object constructors and self references

1476 F# Compiler Setting static field in generic type fails to resolve in typechecker

1365 F# Compiler unexpected Warning 65 in the absence of structural hashing and comparison

1472 F# Compiler double quote breaks comment parsing (F# not fully compatible with OCaml)

338 F# Compiler Incomplete pattern matches should report cases that are not covered. THis could also be used by VS plugin when suggesting completions

1366 F# Compiler Unboxing inconsistencies w.r.t 'null'

1488 F# Compiler Implement redundancy checking for isinst patterns

1322 F# Compiler Unhandled Exception using --clr-mscorlib option

1470 F# Compiler Misleading warning "unit was constrained to FSharp.Core.Unit"

1484 F# Compiler Internal Error when compiling a try-with construct

1342 F# Compiler Bad error messages when constructs can't be quoted

1433 F# Compiler Count of supplied parameters incorrect in error message if named parameters are used.

1060 F# Compiler Quotation error: type varaible not found in environment

1463 F# Compiler quotation of a private value error is not reported until codegen, so is not reported in Visual Studio

1445 F# Compiler Failure when generating code for generic interface with generic method

1390 F# Compiler Pattern matching with bigint literals does not work

1105 F# Compiler params attributes not supported

1279 F# Compiler Constructor call with the object-initialization-expr should error

1278 F# Compiler Unresolved generic constructs in quotations should error instead of warn.

609 F# Compiler not printing explicit type constraints on type declarations

1101 F# Compiler spurious warning:This construct causes code to be less generic than indicated by the type annotations. The type variable 'd has been constrained to be type 'System.IDisposable'.

1502 F# Compiler implement a "sealed" attribute

1533 F# Compiler Support "static let"

1233 F# Compiler cyclic inheritance bug

1452 F# Compiler Complete checks for structs

1529 F# Compiler Incorrect IL generated for call to static method on a valuetype

1490 F# Compiler can't use typeof in attributes

997 F# Compiler Unable to implement interfaces with generic methods

1570 F# Compiler New testcase for "CustomAttributes on Module" failing

1392 F# Compiler Space should not be required between : and >

1437 F# Compiler Assembly attribute w/array parameter fails to build

1497 F# Compiler Quotations 'bindCtor' issue (WebTools & F# 1.3.9.14)

1622 F# Compiler Cannot use "null" in Attributes

1258 F# Compiler Consider design for parser and typechecker that doesn't require exceptions

1050 F# Compiler dot notation on datatype constructors is not resolved

1094 F# Compiler implement internal/public access control escape checks

1160 F# Compiler object should be evaluated eagerly when using a method as a first class function

1304 F# Compiler Explicit program entry point

1500 F# Compiler support conditional compilation (e.g. DEBUG, CODE_ANALYSIS)

1541 F# Compiler finally clause in seq is called out of order, before yield

1590 F# Compiler printf formats should reveal enough information to allow a reasonable implementation of using them in active patterns to scan strings

1644 F# Compiler Generated .fsi is wrong for optional (?-style) parameters

791 F# Compiler OverloadID attributes not in generated .fsi files

1552 F# Compiler Internal error: badly formed Item_ctor_group.

1332 F# Compiler ReflectedDefinition returning null should be allowed

1542 F# Compiler Repeated type argument not being inferred: type Graph <'a> = HashMultiMap<'a,'a>

959 F# Compiler TOp_asm in pattern match

1254 F# Compiler Spurious casts being inserted for upcast operations

1379 F# Compiler Property setter notation be used with mutable .NET fields and mutable record fields

1754 F# Compiler Indexer access should use overloading and not warn on generic constraint

721 F# Compiler suggested undentation from Robert pickering

1398 F# Compiler indentation rule is too strict

1458 F# Compiler Using anonymous type variable in attribute should either not be allowed or should do the right thing

1762 F# Compiler Method name resolution should consult intrinsic methods first

1764 F# Compiler Apply ADJACENT_PREFIX_MINUS rules to '+' as well.

1765 F# Compiler members being printed in reverse alphabetical order in signatures

1766 F# Compiler Treat all the funky operator names like '.[]<-' as single tokens.

1767 F# Compiler Desugaring of computation expressions should make use of operator overloading

1768 F# Compiler Allow the definition of immutable structs using the implicit construction syntax

1526 F# Compiler Possible Regression in FSforScientists sample

1165 F# Compiler Support return attributes?

1660 F# Compiler testcase failure: fsharp\core\verify

1608 F# Compiler Testcase failure: MutateStructFieldOnPropertySet

1112 F# Compiler Bug in definition of generic interface

1625 F# Compiler fsc generates assemblies that don't load/peverify

1683 F# Compiler dispatch slot checking in object expression manages to match non-virtual member

1730 F# Compiler F# allows Enums over string type (Does not PEVerify)

1743 F# Compiler incorrect resolution of overrides

1748 F# Compiler Internal Error: when calling a base member

1749 F# Compiler Interfaces should not allow implicit construction pattern. Bad codegen.

1431 F# Compiler 'end' token ambiguity for interface/class: Incorrect and unactionable error messages when defining class which just implements an interface

1148 F# Compiler class that only implements interface is parsed/recognised as interface (#light)

1275 F# Compiler signature matching is comparing declared interfaces not interface sets

1553 F# Compiler Checks associated with internal and private modules are not yet fully implemented.

1802 F# Compiler Give error messages a "default" number to ensure consistent error message format

1803 F# Compiler Field fails to hide property in parent

732 F# Compiler private modules still appearing in VS object browser

1797 F# Compiler large error range when solving constraint

942 F# Compiler mutable variable escapes its scope when used with an event handler

1505 F# Compiler Need to generate 'GetHashCode' on eception types

1707 F# Compiler MaxInt+1 numeric literals should error

1530 F# Compiler Attributes on properties should not also be put on getter and setter

1077 F# Compiler closing brace following generic type bracket is syntax error without whitespace (lexed into symbolic token).

1831 F# Compiler Finally block called twice for nested sequence comprehensions

1854 F# Compiler TestCase Failure: Peverify error in Adcenter Adpredictor sample

1763 F# Compiler Resolution to extension members should look through the entire hierarchy of a type

1363 F# Compiler Need to apply DefaultMemberAttribute to enable C# to consume F# Indexers

313 F# Tools fsyacc --ml-compatibility: Does not open the Parsing module

1425 F# Library check structural equality on non-zero based multi-dimensional arrays

932 F# Library Fix NaN equality on structured terms and make structural equality go through object.Equals

1059 F# Library "=" doesn't work with 2D array values

1311 F# Library NullReferenceException in StructuredFormat.Display.leafFormatter

1384 F# Library Suggestion: allow explicit exception type definitions to override members - specifically the Message property

975 F# Library Map Exceptions: when a key is not in a map, we get an index out of range exception instead of something like KeyNotFoundException

1572 F# Library support "enum" and "delegate" type decomposition constraints for typesafe generic code over enums and delegates (e.g. conversions and event creation)

1591 F# Library Bug in Async.Generate reported by David M Peixotto [dmp@rice.edu]

1598 F# Library Problem with Array.fold1_left

1571 F# Library Allow "int" and "char" functions to convert to/from int/char

1740 F# Library problem with Lazy.SynchronizedForce()

1760 F# Library Implement Unchecked.defaultof<_> (delete LanguagePrimitives.DefaultValueUnchecked)

1761 F# Library Implement IComparable and Equals on Math.Complex

1374 F# Library compelx number pretty printing broken

1776 F# Library async implementation hits tail recursion problems on 64-bit

1796 F# Library ThreadAbort is causing PostSync to raise exception

1800 F# Library implement decimal literals, "decimal" conversion function

1801 F# Library implement 'sign' and 'round' overloaded functions in library

1804 F# Library sprintf "%A" formats should print decimal literals in "g" format rather than any specific floating point format

1731 F# Library Need a way to cast primitive types to/from enums

1108 F# Library Langauge Specifiation Issue: Enumerations don't convert to int32, uint32 etc.

1861 F# Library lock function can be used on integers

1791 F# Perf Apply fold_left performance improvements to research branch (for Morgan Stanley)

1819 F# Testing Test error in tmptest.mli

1467 F# Language AbstractClass attribute

1599 F# Language Dispose not called in 'use _ = foo()'

1248 F# Language symmetric overloading

339 F# Language Support generic recursion

1558 F# Language Should offside rule be applied to last clause of match?

1633 F# Language Compiler override of Equals should not throw exceptions, per .NET coding guidelines.

1638 F# Language When functions are use/passed as values, we should not insist on optional values.

755 F# Language support marshalling parameter and return attributes

1395 F# Language parsing of minus (-)

641 F# Language Deprecate (type ty) syntax

1614 F# Language Should classes, unions, exceptions, records etc have a way to _not_ be marked with Serializable?

1535 F# Visual Studio Tooltip for List.combine claims "List.combine is a synonym for List.combine"

1651 F# Visual Studio Public constructors of non-visible types are showing up in top-level completion list

1652 F# Visual Studio Namespaces with no visible members at a given program point should not be included in completion list