Welcome to F#. F# is a programming language for the .NET platform with a design based on the ML programming language with extensions to access .NET libraries. This release contains
Full details can be found at the F# website. There you can find details about F# resources and community sites. You may be interested in tracking Don Syme's F# blog and/or blogging about your experiences with using F#.
Installation:
Using and Learning:
Troubleshooting:
You may run F# programs in conjunction with other CLI implementations such as Mono. Please read the notes below. Some more information is available at the F# Wiki.
F# On Mono
Library
type System.Net.WebRequest with member x.GetResponseAsync() = Async.BuildPrimitive(x.BeginGetResponse, x.EndGetResponse)
2033 F# Compiler doesn't work with type forwarders found in .NET Framework 3.5 SP 1 Beta1
Library
2033 Code Dom Error Reporting not working in 1.9.4.15, reported by Tomas Petricek (thanks Tomas!) 2062 Inapplicable error message on attributes on method parameter, reported by David Crocker (thanks David!) 2101 Performance regression with regard to inlining, reported by Stephan Tolksdorf (thanks Stephan!) 2108 Fix intellisense regression in 1.9.4.15 due to mistaken release of experimental code, reported by Howard Mansell (thanks Howard!) 2125 Stack overflow in structural equality test on 64-bit CLR, reported by Stephan Tolksdorf (thanks Stephan!) 2128 Regression: cannot convert the following F# Quotation to a LINQ Expression Tree
Language
Library
Tools
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
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,
open Microsoft.FSharp.Math let f7 (x:Matrix<_>) y = x + y
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.
.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#:
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>
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.
The keyword global has been reserved for future use by F# code
'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.
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 Arguments 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
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.
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
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.
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 marsahlling 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
This release includes a prototype of access controls, a feature that lets you use the keywords internal and private to mark values, members, record fields and union constructors with restricted visibility. For example:
let internal x = 1 type MyRecord = { private x : int } member obj.TwiceX = obj.x + obj.x static member Create(n) = { x = n }
CAVEAT: a number of checks are not yet implemented. In particular, if you mark a type or a module with private or internal you will get a warning that the checks for these kinds of constructs are not yet complete.
Type augmentations separated from a main type definition now give a warning if they contain interface implementations or implementations of overrides (see TypeAugmentations). This is because of the unsoundess mentioned as part of the specification, in particular "Augmentations that define interface implementations and override defaults may result in undefined behaviour if the intervening code creates instances of the object at runtime and makes calls via the virtual dispatch slots implemented by these functions (ill-defined recursion cycles can in principle occur)." This feature may be deprecated in a future release of the language.
The "static optimization" language construct now gives a warning it is used outside the implementation of the F# library. Use of this construct requires extreme care and if misused can cause unverifiable to be genreated or lead to the behaviour of code varying under different optimization settings.
F# now permits classes (including those with implicit constructors) to have uninitialized val fields marked with DefaultValueAttribute. This is primarily for codedom codegen. These fields will be initialized to null or the zero-bit-pattern for structs. These may also be marked static val. As a result we also implemented the setting of static F# fields.
Add EDITING define to Visual Studio mode: this is ON as far as Visual Studio editing is concerned, but OFF for compilation and execution. This is useful when mimicking generated codedom code in a partially implemented file
Constructed class types may now be mutually recursive with other types.
Multiple custom attributes can now be specified using repeated uses of [< ... >], as well as via the previous technique where a single [< ... >] is used with semicolon separation.
Ranges such as [ 1.1 ... 100.1 ] over floating point numbers are now deprecated. The floating point computations here are inherently highly sensitive and the resulting sequences often end up having a non-intuitive number of elements under any reasonable specification. For now use an integer range and convert to floating point. We expect to add a 'linspace' operator giving size and number of divisions in a future release.
Add prefix operators "%" and "%%" as ASCII-equivalent for Unicode section marker operator used for splicing into quoted expressions (i.e. for data binding into quotations expressions).
The F# libraries have been renamed: fslib.dll becomes FSharp.Core.dll and mllib.dll becomes FSharp.Compatibility.dll. This renaming has been planned for some time in order to give F# DLLs more standard .NET library names and we believe it will help us to grow the library further as time goes on.
Move some less important collections to FSharp.Compatibility.dll.
Move the Int32, Int64 etc. modules to FSharp.Compatibility.dll, though they currently remain in the Microsoft.FSharp.Core namespace.
Deprecate the undocumented Microsoft.FSharp.Core.Func module.
Some additional identifiers have now been marked with the 'OCaml compatibility' attribute
Added Permute to vector type, PermuteRows, PermuteColumns to matrix type.
Improved Library Documentation. Thanks to Ralf Herbrich for helping us complete minimal documentation for each funciton in the library.
Add Async.Parallel2 and Async.Parallel3 to Microsoft.FSharp.Control
Add more members to BigInt, Add modulus operator to BigInt.
Add INormFloat for generic distance algorithms.
Add WebRequest.GetResponseAsync to Microsoft.FSharp.Control.CommonExtensions.
FxCop: Fix some low hanging fruit so fewer errors are reported
Adjust ConcurrentLife sample so it compiles and runs correctly
Update F# Linq samples to match the Expert F# book and to work with Orcas Beta2
Improved ASP.NET sample help
Add command line option to permit the name of mscorlib.dll to be set. For use with Singularity.
1007 F# Compiler Improve error message for property member without "this" (was internal compiler error). 1061 F# Compiler Two property nodes being generated for get/set metadata 1103 F# Compiler Bad code generation for active patterns from fswebtools example 1104 F# Compiler *** WARNING: basic block at end of method ends without a leave, branch, return or throw. Adding throw 1111 F# Compiler fswebtools code doesn't verify 1112 F# Compiler Cannot define an interface with a generic method 1116 F# Compiler Active pattern example fails where function is defined by matching (Reported by Andrew Kennedy - thanks Andrew!) 1117 F# Compiler Bindings that look like function definitions are interpreted as pattern matches without warning. Add a warning for this case to allow for later language design change here. 1121 F# Compiler Allow attribute targets "module", "type", "assembly", "class", "struct", "interface", "delegate", "property", "field" 1126 F# Compiler Unverifiable code generated by TLR and other optimizations when used with constrained local generic functions 1253 F# Compiler Implement optimizations related to 'when' filters in sequence expressions 1255 F# Compiler Make static optimization construct library-only (give a warning) 1261 F# Compiler CodeDom bug: FieldTest is failing in codedom suite 1263 F# Compiler Nested mutable structs not being mutated correctly 1264 F# Compiler bignum/bigint literals don't meet specification, e.g. f -3N is accepted when it should be f (-3N) like all other integer forms 1265 F# compiler Deprecate #hardwhite and #white (old names for #light) 1266 F# Compiler Internal values are being optimized away in cross-module situations 1271 F# Compiler Internal and Private keywords in 1.9.33 1272 F# Compiler New-ing a sub class with unimplemented abstract members should not be allowed (reported by Keiko Nakata - thanks Keiko!) 1300 F# Compiler [FS 1.9.3.7] Issue with bindMethodBySearch & bindProp in quotations.fs 1324 F# Compiler [FS 1.9.3.7] Bad binary created when implementing COM interop interface. (Gacutil: Method does not have an implementation) 1318 F# Compiler [FS 1.9.3.7] Weird behavior instantiating .NET objects 1317 F# Compiler [FS 1.9.3.7] assembly private internal fields are leaking across assemblies through inline 1238 F# Compiler Bug reported by Tomas Petricek: give error when extension members defined in a namespace 1336 F# Compiler constraints "struct" and "not struct" do not work in #light code, and "not struct" never works 972 F# Interactive FSI reports "error: forceM: - envGetMethB: failed" 982 F# Interactive Visual Studio FSI doesn't function correctly on some machines. Force session to be in UTF8. This fixes a codepage related bug that rendered input/output invisible. 984 F# Interactive --exec doesn't function correctly 1027 F# Interactive Incorrect parsing of some expressions. Expr interactions were dropping lookahead tokens. 1053 F# Interactive Bug accessing XLinq op_Explicit method from FSI 1198 F# Interactive #time ms printing 2d not 3d 1033 F# Visual Studio Some errors related to DLL references were being swallowed into Visual Studio (reported only once then swallowed into cache). 1037 F# Visual Studio Adjust "incomplete matches warning" so it underlines less intrusively in visual studio. 1100 F# Visual Studio Missing DLLs specified in project arguments not reported correctly in Visual Studio error window 1106 F# Visual Studio VS mode is swallowing some errors that reporting that a DLL can't be found 1303 F# Visual Studio F# addin global keybinding localisation work around - failing on KoreanOS/DefaultVS. 1095 F# Samples Ensure FLinq really works under Orcas Beta2 1267 Samples PersonalWebSite sample is busted (reported by Tomas Petricek) 1268 Samples F# version number not being added to web.config files in samples 1118 F# Library Scaling operator missing from matrix class (reported by Martin Szummer - thanks Martin!) 1017 F# Library Range comprehensions fixes for wraparounds and min/max int conditions. Exact number of steps with floats where possible. 1051 F# Library Calls to F# members should be quoted as MethodCall and PropertyCall nodes in the quotation tree 1127 F# Library foldByCol and foldByRow are incorrectly implemented in F# matrix library 1129 F# Library Add RowVector.zero to the F# matrix library 1080 F# Library Seq.cache_all does not have the properties of cache. Deprecate Seq.cache_all, add Seq.cache to return a IDisposable handle 1215 F# Library bigint now has properties which are printed by fsi. 1293 F# Library HTML docs hrefs to lower case types not always correct 1052 F# Tools Fix numerous issues with CodeDom code generation: Generate static fields, Remove old hacks for generating static fields, Generate type code snippets,Fix order of compilation of files.
The following syntax forms
expr := ... | e1.[range1] | e1.[range1,range2] range := e2..e3 | ..e3 | e2.. | *
represent slicing from a 1D or 2D data structures that supports associative lookup on ordered keys. In practice these forms can be used with
For example:
let s1 = "abcdef" s1.[*] = s1 s1.[0..] = s1 s1.[1..] = "bcdef" s1.[2..] = "cdef" s1.[5..] = "f" s1.[6..] = "" let m1 = matrix [ [ 1.0; 2.0; 3.0; 4.0; 5.0; 6.0 ]; [ 10.0;20.0;30.0;40.0;50.0;60.0 ] ] m1.[*,*] = m1 m1.[0..,*] = matrix [ [ 1.0; 2.0; 3.0; 4.0; 5.0; 6.0 ]; [ 10.0;20.0;30.0;40.0;50.0;60.0 ] ] m1.[1..,*] = matrix [ [ 10.0;20.0;30.0;40.0;50.0;60.0 ] ] m1.[*,1..] = matrix [ [ 2.0; 3.0; 4.0; 5.0; 6.0 ]; [ 20.0;30.0;40.0;50.0;60.0 ] ])
The primary restriction is that the syntax can't yet be used to extract a vector from a matrix, e.g.
m1.[1,*] m1.[*,1]
are not yet valid syntactic forms. These would have to map to different operators.
Technically speaking, the above represent calls to the Microsoft.FSharp.Core.Operators operators
val (.[..]) : ^src -> 'idx option -> 'idx option -> ^res val (.[..,..]) : ^src -> 'idx1 option -> 'idx1 option -> 'idx2 option -> 'idx2 option -> ^res
i.e.
e1.[e2..e3] == (.[..]) e1 (Some e2) (Some e3) e1.[e2..] == (.[..]) e1 (Some e2) None e1.[..e3] == (.[..]) e1 None (Some e3)
The implementation of (.[..]) requires:
member GetSlice : 'idx option * 'idx option -> 'res
member Item : 'idx -> 'elem with get
Similar conditions are used for the 2D lookup operator.
Members (but not let-bound functions) may have optional arguments. These must come at the end of the argument list. An optional argument is marked with a ? before its name. Inside the member the argument has type option<argType>. On the callside the argument typically appears to have type argType, though there is a way to pass a value of type option<argType> if necessary (see below).
let defaultArg x y = match x with None -> y | Some v -> v type T() = static member OneNormalTwoOptional (arg1, ?arg2, ?arg3) = let arg2 = defaultArg arg2 3 let arg3 = defaultArg arg3 10 arg1 + arg2 + arg3 static member TwoOptional (?arg1, ?arg2) = let arg1 = defaultArg arg1 3 let arg2 = defaultArg arg2 10 arg1 + arg2
In a signature optional arguments appear as follows:
static member OneNormalTwoOptional : arg1:int * ?arg2:int * ?arg3:int -> int static member TwoOptional : ?arg1:int * ?arg2:int -> int
Callers may specify optional arguments either:
For example:
T.OneNormalTwoOptional(3) T.OneNormalTwoOptional(3,2) T.OneNormalTwoOptional(arg1=3) T.OneNormalTwoOptional(arg1=3,arg2=1) T.OneNormalTwoOptional(arg2=3,arg1=0) T.OneNormalTwoOptional(arg2=3,arg1=0,arg3=11) T.OneNormalTwoOptional(0,3,11) T.OneNormalTwoOptional(0,3,arg3=11) T.OneNormalTwoOptional(arg1=3,?arg2=Some(1)) T.OneNormalTwoOptional(arg2=3,arg1=0,arg3=11) T.OneNormalTwoOptional(?arg2=Some(3),arg1=0,arg3=11) T.OneNormalTwoOptional(0,3,?arg3=Some(11))
Optional arguments can't be used in member constraints. They can be used in interface and abstract members. The compiled representation of optional arguments is fragile, in the sense that the addition of further optional arguments to a member signature will result in a compiled form that is not binary compatible with the previous compiled form.
Calls to members (but not let-bound functions or function values) may use named arguments. For example
System.Console.WriteLine(format="Hello {0}",arg0="World") System.Console.WriteLine("Hello {0}",arg0="World") System.Console.WriteLine(arg0="World",format="Hello {0}")
Named arguments may not be used with the curried arguments of a member (only the initial set of "tupled" arguments).
Named arguments must appear after all other arguments:
System.Console.WriteLine(arg0="World","Hello {0}")
The names of members may be listed in signatures and on the signatures used for abstract members, e.g.
static member ThreeArgs : arg1:int * arg2:int * arg3:int -> int abstract TwoArgs : arg1:int * arg2:int -> int
Recall that F# automatically implements comparison and hash semantics for tuple, record and discriminated union types (only). Now F# will also implement Object.GetHashCode and Object.Equals for these types. For example:
> let x = box (1,2);; val x : obj > x.GetHashCode();; val it : int = 5 > let x2 = box (1,2);; val x2 : obj > x2.GetHashCode();; val it : int = 5 > x2.Equals(x);; val it : bool = true
This means System.Collections.Generic.Dictionary etc. will now be usable with tuple, record or discriminated union keys with the expected semantics without supplying a custom comparer. This eliminates a common source of confusing and possible unexpected behaviour.
Bugs fixed:
977 Fix: resx files compiled without change to cwd. Fix: FSI reflection emit support for constrained callvirt opcodes. Fix: Hashtbl copying bug. Fix: nested module/type loading bug. Fix: performance improvements to BigNat and BigInt.
New keyword. The identifier use is now a keyword.
use bindings for deterministic resources. In expressions the binding form use x = expr in body is equivalent to using expr (fun x -> body). Otherwise the binding form has the same behaviour as let, e.g. the in can be dropped when using the #light syntax option.
Reserve keywords for future syntax extensions. All identifiers ending with ! and ? are now reserved for future language extensions.
Reserve keywords for accessiblity extensions. The identifier internal is now reserved as a keyword.
Deprecate old defaulting scheme for overloaded operators.. Once upon a time all statically-resolved overloading operators used an implicit default of int. THis default can now be explicitly defined using a series of default constraints. The old scheme has been removed. This should result in better error messages in many situations.
Can now use flag1 ||| flag2 as constants in custom attributes. e.g.
[]
Experimental Asynchronous Computation Library. See Microsoft.FSharp.Experimental.Control.
Experimental Workflow/Monadic Syntax. See Microsoft.FSharp.Experimental.Control.
Bugs fixed:
951 11/05/2007 F# Visual Studio Plugin Major intellisense failure 830 11/05/2007 F# Visual Studio Plugin Intellisense should show enum values in hover over or "dot" lookup on an enum typed-value 926 11/05/2007 F# Compiler assertion failure in tc.ml 531 11/05/2007 F# Language Cannot use "A ||| B" in enum flags when specifying a custom attribute (not a recognised constant) 937 10/05/2007 F# Language enums on not int32 types? e.g. int64. 948 04/05/2007 F# Compiler xml docs are getting dropped on properties 946 02/05/2007 F# Compiler comment lexing does not handle quotes and backslashes inside verbatim strings 945 02/05/2007 F# Compiler comment lexing does not handle quotes inside quoted strings 944 01/05/2007 F# Language Need to split IDelegateEvent into IPrimitiveDelegateEvent and IDelegateEvent (for CodeDom) 936 01/05/2007 F# Compiler custom attributes with enum typed properties generate invalid IL code. 934 01/05/2007 F# Compiler undescore and uppercase variables may not be used as arguments to object constructors 935 01/05/2007 F# Compiler name clashes in generated field names (a/a0) 17/05/2007 F# Compiler fix minor bugs preventing F# Interactive from running on Mono 17/05/2007 F# Compiler fix bug with code generation for pattern matching on structs 17/05/2007 F# Compiler make Microsoft.FSharp.Math.Complex a struct 17/05/2007 F# Compiler Check virtual slots are revealed in signatures if the type is revealed to have an object-model representation 17/05/2007 F# Compiler Fix two Visual Studio crashes due to recursive class hierarchies and recursive type abbreviations 17/05/2007 F# Compiler Intellisense for F#-defined enum types 17/05/2007 F# Compiler Fix bad error mark in Visual Studio for missing DLL reference 17/05/2007 F# Compiler Fix long comment parsing 17/05/2007 F# Compiler Fix install on Orcas Beta1 17/05/2007 F# Compiler Fix minor problem with DLinq sample 17/05/2007 F# Compiler Minor fixes for Microsoft.FSharp.Experimental.Control.Async 17/05/2007 F# Compiler Allow access to the types implicity defined by 'exception' declarations 17/05/2007 F# Visual Studio Plugin Auto install for VS versions 7.1, 8.0 and 9.0 (Orcas Beta 1)
Overloaded numeric conversion functions. The functions int, float, float32 etc. now overloaded conversion operators. For example, you can replace Int32.of_float and Int32.of_int64 and all similar operators that convert to int by just int (or int32, which is equivalent). Likewise for sbyte, byte, int16, uint16 int32, uint32, int64, uint64, nativeint, unativeint, float, float32, single, double.
Checked arithmetic Open Microsoft.FSharp.Core.Operators.Checked to get checked arithmetic versions of +, -, * and the above conversion operators.
Updated FLinq sample The FLinq sample has been updated to use the technique outline in Don Syme's ML Workshop LINQ paper from 2006. More details to follow in blog entries.
Bugs fixed:
895 "type" links broken on F# manual pages online 893 unicode lex bug 0x800 - 0x7ff 894 file hashes not being added to assembly resouce 900 --quotation-data flag problems causing problems for many things in F# Interative from Visual Studio 903 catch and report exceptions on user threads without killing fsi 907 quotation API TopDefnData should use an Assembly value. 909 Add Seq.cache_all 910 Seq.filter is broken on 64-bit 911 Fast F# cannot be unset, and should be ignored anyway 898 Invislbe error location information reported in Visual Studio 912 {} still gives tyref_of_stripped_typ 914 Pattern matching code causes NullReferenceException 913 Nested enums not showing up under intellisense, e.g. System.Environment.SpecialFolder 904 explicit instantiation of generic type not found 905 Intellisense failure for static method in generic collection class 918 Poor error when generic type given the wrong number of type arguments 916 Should not be printing to stderr on "fsi --exec" 915 Add updated LINQ sample (updates are on Don’s laptop) 924 structural comparison should fail for incomparable types 923 Add Array2.rebase 922 Use operator overloading for colelction types Tagged.Set, Tagged.Map, Tagged.HashSet, Tagged.HashMultiMap
This release includes a preview version of ``implicit class construction'' or the ``compact class syntax''. An class has an implicit constructor if arguments are present following its type declaration. The arguments to the implicit constructor follow the type name, then a sequence of let-bindings (prior to the members) define the object initialisation. Note, the let bindings are in scope over the members, but they are private to this class. For example:
type File1(path) = class let innerFile = new FileInfo(path) member x.InnerFile = innerFile end let myFile1 = new File1("file.txt")
Classes with an implicit constructor have zero, one or more arguments such as path after the name of the type constructor. Furthermore the body of the class may contain let bindings, which are always private to the class, indeed private to the object (you can't access the let bindings even for other values of the same type). Above we have also added the a property member InnerFile to reveal the value of the value of the innerFile. Here is a second example:
type Counter(start, increment, length) = class let finish = start + length let mutable current = start member obj.Current = current member obj.Increment() = if current > finish then failwith "finished!"; current <- current + increment end
Logically speaking, this class is equivalent to:
// The above code is equivalent to the following: type Counter = class val start: int val increment: int val length : int val finish : int val mutable current : int new(start, increment, length) = { start=start; increment=increment; length=length; finish = start + length; current = start; } member obj.Current = current member obj.Increment() = if obj.current > obj.finish then failwith "finished!"; obj.current <- obj.current + obj.increme end
The first definition of Counter is one third the size of the second, so obviously the syntax has some advantages. Indeed we believe this feature is part of the key to enabling mixed OO/functional programming in practice.
Note this feature is in many ways similar to those found in OCaml and other langauges.
Classes can include both implicit and explicit constructors
When using implicit class construction the call to the base class constructor is specified as part of the inherits declaration itself.
type Base1(state:int) = class member x.State = state end type Sub1(state:int) = class inherit Base1(state) member x.OtherState = state end let myOtherObject = new Sub1(1)
The types may be generic. Type annotations are generally required:
type Base1<'a>(state:'a) = class member x.State = state end let myObject1 = new Base1<int>(1) let myObject2 = new Base1<string>("1")
Known Limitation: The F# compiler is free to optimize and represent the let-bindings in anyway it deems fit. However, in this release each ``let'' binding gives rise to a field in the class. This is the main reason why we say this feature is only a ``tech-preview'' - while it is extremely useful even as it stands you have to be a little careful not to put thousands of helper (e.g. closed-form) ``let'' bindings in a critical class. In the above example, the start value is only required during initialization of the object and thus should in principle not be included as a field of the object.
Known Limitation: In this release classes with implicit constructors may not be mutually recursive.
Known Limitation: Patterns may not be used in the arguments to the constructor.
Known Limitation: The bindings are established in order. However base class constructors written in other .NET languages can in principal call virtual members prior to the complete initialization of the object. This means you should be careful about using your ``let'' bindings from override members that may be activated by base classes during base initialization. In this case the behaviour is unspecified and the 'default' or 'zero' values of some of the let bound variables may be observed.
Active patterns give a form of extensible pattern matching on abstract values. F# active patterns allow you to pattern match against .NET object values such as XML, System.Type values and LINQ Expression trees. They allow a function to give a complete decomposition of an abstract type by projecting it into a union type.
Active Decomposition. An active pattern discrimination function declares a ``view'' on a type. It is a function whose name includes surrounding (| ... |) marks. It gives a technique for automatically decomposing the values of a type when the enclosed label(s) are used as pattern discriminator(s). For example:
open Microsoft.FSharp.Math let (|Rect|) (x:complex) = Rect(x.RealPart, x.ImaginaryPart) let (|Polar|) (x:complex) = Polar(x.Magnitude , x.Phase)
These functions declare two unrelated discriminators for viewing the (abstract) type of complex numbers as tuples in rectangular and polar coordinates. Multiplication can now be expressed using either coordinate system:
let mulViaRect c1 c2 = match c1,c2 with | Rect(ar,ai), Rect(br,bi) -> Complex.mkRect(ar*br - ai*bi, ai*br + bi*ar) let mulViaPolar c1 c2 = match c1,c2 with | Polar(r1,th1),Polar(r2,th2) -> Complex.mkPolar(r1*r2, th1+th2)
In the first case, the decomposition function (|Rect|) is run when the inputs c1 and c2 are matched.
Because Rect and Polar are simply pattern discriminators they can be used in any position where an existing pattern discriminator can be used:
let mul2 (Polar(r1,th1)) (Polar(r2,th2)) = Complex.mkPolar(r1*r2, th1+th2) for (Polar(r,th)) in myComplexNumberList do printf "r = %O, th = %O" r th
Active Discrimination. Active pattern discrimination functions may include multiple active pattern labels. For example, the following defines a function (|Named|Array|ByRef|Ptr|Param|) that discriminates and decomposes System.Type values into one of the five indicated cases:
open System let (|Named|Array|ByRef|Ptr|Param|) (typ : System.Type) = if typ.IsGenericType then Named(typ.GetGenericTypeDefinition(), typ.GetGenericArguments()) elif not typ.HasElementType then Named(typ, [| |]) elif typ.IsArray then Array(typ.GetElementType(), typ.GetArrayRank()) elif typ.IsByRef then ByRef(typ.GetElementType()) elif typ.IsPointer then Ptr(typ.GetElementType()) elif typ.IsGenericParameter then Param(typ.GenericParameterPosition, typ.GetGenericParameterConstraints()) else failwith "unexpected System.Type"
You can now use these discrimination labels in pattern matching:
let rec toString typ = match typ with | Named (con, args) -> "(" + con.Name + " " + String.Join(";",Array.map toString args) + ")" | Array (arg, rank) -> "(Array" + rank.ToString() + " " + toString arg + ")" | ByRef arg -> "(ByRef " + toString arg + ")" | Ptr arg -> "(Ptr " + toString arg + ")" | Param(pos,cxs) -> "(Param " + any_to_string (pos,cxs) + ")"
This is by no means the only view that could be applied to this type, but it is a useful one when performing operations that are sensitive to the presence of type variables (Param) and generic type constructors (Named).
Active Patterns in the F# Library The F# library currently only includes active patterns for a few constructs. One is the LazyList type, a cached, lazily-computed list. This allows you to pattern match on LazyList values, including nested patterns, e.g. the following code performs pairwise summation on a lazy list:
open LazyList let rec pairReduce xs = match xs with | Cons (x, Cons (y,ys)) -> LazyList.consf (x+y) (fun () -> pairReduce ys) | Cons (x, Nil ()) -> LazyList.cons x (LazyList.empty ()) | Nil () -> LazyList.empty ()
Partial Active Patterns. Partial patterns are signified by adding a |_| to the name of the discrimination function. The body of the discrimination function must return a value of type 'option'. They are most useful when dealing with repeatedly recurring queries on very "heterogeneous" data sets, i.e. data sets able to represent a large range of possible entities, but where you're often interested in focusing on a subset of the entities involved. Strings, term structures and XML are common examples. Here is an example when matching on integers:
let (|MulThree|_|) inp = if inp % 3 = 0 then Some(inp/3) else None let (|MulSeven|_|) inp = if inp % 7 = 0 then Some(inp/7) else None let example1 inp = match 21 with | MulThree(residue) -> printf "residue = %d!\n" residue | MulSeven(residue) -> printf "residue = %d!\n" residue | _ -> printf "no match!\n" example1 777 example1 9 example1 10 example1 21
Partial patterns are by their nature incomplete and thus the benefits of completeness checking in pattern matching are almost completely lost when using these patterns.
Parameterized Active Patterns. So far all the pattern discrimination functions have taken one argument, i.e. the input being discriminated. Pattern discrimination functions may also take additional arguments that represent parameters to the pattern:
let (|Equal|_|) x y = printf "x = %d!\n" x if x = y then Some() else None
When used in a pattern a parameterized pattern tag may be followed by simple expressions that represent arguments, and then finally followed by the pattern being matched against, e.g.:
let example1 = match 3 with | Equal 4 () -> printf "3 = 4!\n" | Equal 3 () -> printf "3 = 3!\n" | _ -> printf "3 = ?!\n"
Note: not all expressions can be used in this position. Only identifiers, tuples, applications, lists and array expressions may be written in this position. Here is a second example. Extra parentheses can be useful to distinguish pattern parameters from the variables being bound by the pattern:
let (|Lookup|_|) x map = Map.tryfind x map let example2 = match Map.of_list [ "2", "Two" ; "3", "Three" ] with | Lookup("4")(v) -> printf "4 should not be present!\n" | Lookup("3")(v) -> printf "map(3) = %s\n" v | Lookup("2")(v) -> printf "this should not be reached\n" | _ -> printf "3 = ?!\n"
Values with the names of the form (| ... |) can in theory all be used as parameters, e.g.
let mapQ1 f (|P|_|) = function (P x) -> Some (f x) | _ -> None
However this is not a common technique. Our final example shows the use of a simple set of active patterns to match on the concrete structure of an XML document and extract a recursive algebra from the document:
open System.Xml open System.Collections open System.Collections.Generic let Select (|P|_|) (x: #XmlNode) = [ for P y as n in x.ChildNodes -> y ] let Select2 (|A|B|) (x: #XmlNode) = [ for (A y | B y) as n in x.ChildNodes -> y ] let (|Elem|_|) name (inp: #XmlNode) = if inp.Name = name then Some(inp) else None let (|Attr|_|) attr (inp: #XmlNode) = match inp.Attributes.GetNamedItem(attr) with | null -> None | node -> Some(node.Value) let (|Num|_|) attr inp = match inp with | Attr attr v -> Some (Float.of_string v) | _ -> None type scene = | Sphere of float * float * float * float | Intersect of scene list let (|Vector|_|) = function (Num "x" x & Num "y" y & Num "z" z) -> Some(x,y,z) | _ -> None let rec (|ShapeElem|_|) inp = match inp with | Elem "Sphere" (Num "r" r & Num "x" x & Num "y" y & Num "z" z) -> Some (Sphere (r,x,y,z)) | Elem "Intersect" (ShapeElems(objs)) -> Some (Intersect objs) | _ -> None and (|ShapeElems|) inp = Select (|ShapeElem|_|) inp let parse inp = match (inp :> XmlNode) with | Elem "Scene" (ShapeElems elems) -> elems | _ -> failwith "not a scene graph" let inp = "<Scene> <Intersect> <Sphere r='2' x='1' y='0' z='0'/> <Intersect> <Sphere r='2' x='4' y='0' z='0'/> <Sphere r='2' x='-3' y='0' z='0'/> </Intersect> <Sphere r='2' x='-2' y='1' z='0'/> </Intersect> </Scene>" let doc = new XmlDocument() doc.LoadXml(inp) //print_endline doc.DocumentElement.Name printf "results = %A\n" (parse doc.DocumentElement)
Signatures for Active Patterns The signature for an active discrimination function is given in terms of a function returning a Choice type:
val (|Cons|Nil|) : 'a llist -> Choice<('a * 'a llist),unit>
Notes: Some obvious issues with using active patterns in languages with side effects - how many times are discrimination functions executed? The specification used for F# is that "if pattern matching requires that an active discrimination function be run on a given input, then the pattern matching apparatus is free to run that function on the same input as many times as it wishes". In the presence of active patterns, rules of patterns are searched exhaustively on a left-to-right, top-to-bottom basis. We have chosen this semantics partly because we want to strongly discourage discriminator functions with side effects, in much the same way that side effects are strongly discouraged in code that implements the C# and F# property notations.
Known Limitation: Individual active pattern discrimination functions may decompose using at most 7 labels. Multiple partial active patterns can be used instead. It is planned that this limitation will be lifted in the next release.
Known Limitation: Choice types of size greater than 7 are not implemented in this release.
Much of this work was done by Gregory Neverov in the summer of 2006.
Methods can now be used as first class values without eta-expansion. Context-sensitive type information is used to resolve overloading. For example:
open System.IO let data1 = List.map File.ReadAllLines ["a.txt"; "b.txt"]
If applied to arguments methods must still be applied to a syntactic tuple of arguments where the number of elements in the tuple is the same as the numebr of arguments expected by the method.
This long-standing problem has now been fixed. This only applies if the type arguments follow immediately after a leading constructor without spaces, e.g. C<D<int>> not C <D <int>>.
Until now, F# followed OCaml and give "comma" in parenthesized tupled patterns higher precedence than type constraints. This has changed. Thus we would previously disallow
let f (samplefreq:int64, s:int64)
and previously parse
let f (samplefreq, s : int64)
as
let f ((samplefreq, s) : int64)
(leading to a type error). We are changing this behaviour for parenthesized patterns, and now
let f (samplefreq:int64, s:int64)
will be accepted and parsed in the same way as
let f ((samplefreq:int64), (s:int64))
and the following current code will almost certainly give an error
let f (samplefreq, s:int64*int64)
since it is now parsed as
let f (samplefreq, (s:int64*int64))
Technically speaking this is a breaking change, though we have encountered only one example of user code affected by this. The readability of the user code was massively improved after taking the above change into account, hence in the balance we have decided the improvement to the language is more important in the long term. In all cases there are still reasonable and simple ways to ensure your code cross compiles with OCaml if necessary.
StructLayout, FieldOffset etc. attributes now supported for C interoperability.
F# Lists support IEnumerable/seq. In previous versions the F# list type was not compatible with the .NET IEnumerable<'a> type, which is called seq<'a> in F#. This was because null was used as a representation for lists. This has now been changed. See further discussion below.
Normalization of class and interface constraints. When constraints are used extensively situations can arise where a single type parameter can be constrained by multiple related constraints. For example,
let f1 (x : #seq<('a * 'b)>) = () let f2 (x : #seq<('a * 'b * 'c)>) = () let g x = f1 x; f2 x
Here g is constrained to operate over both sequences of pairs and sequences of triples. While it is in principle possible for a .NET type to implement both of these interfaces, in practice this is never used. In prior versions F# was not normalizing such constraints to ensure that any particular nominal type occurred at only one instantiation for a given collection of constraints. This has now been added to the language specification and has been implemented. This gives earlier detection of errors involving incompatible constraints and ensures that multiple related constraints never need to be listed in signatures should one constraint subsume another.
Lexer literals 32u and 32uL now accepted. These are unsigned 32-bit and 64-bit integers.
new now optional. The construct new Type(args) can now optionally be replaced by simply Type(args). This applies when no ambiguity exists as to which type is being constructed and where no type instantiation is given. However, a warning is given if the type supports the IDisposable interface. For these constructs it is felt that the potential use of resources by the construct means that new Type(args) is much clearer
More F# keywords accepted as identifiers in --ml-compatibility-mode.
Enum values now support the ||| and &&& operators. This applies when the Enum type is marked with the FlagsAttribute attribute.
Extend overload inference for (+), (*) etc. to allow return types to propagate to the overload resolution. This reduces the need for type annotations in many circumstances.
Explicit type applications at member calls and path lookups. You can now explicitly apply type arguments in lookups, e.g.
let x = Map.empty<string,int> // The next is from the Microsoft Research 'Joins' library let subscribe = Asynchronous.CreateChannel<EventSink<'b>>(client.Join)
Flexible printf integer and floating point format enhancements. %d, %u, %i, %x and %o formats can now be used with any integer types. Many of the other format specifiers have now been deprecated or marked 'for OCaml compatibility'. %f and %g and other floating point specifiers can now be used with both Single and Double precision numbers. The use of the format specifiers is still checked at compile time to ensure static type correctness.
Printf format enhancements. * can now be used as a width and/or precision specifier. This corresponds to a 'computed' specifier and an extra integer argument giving the width is required in the argument list for the specifier. For example:
sprintf "|%*s|%*s|" 10 "hello" 8 "world" ;; sprintf "|%*s|%*s|" 12 "hello" 6 "world" ;;
Give:
val it : string = "| hello| world|" val it : string = "| hello| world|"
OCaml Compatibility Warnings. Several constructs in the F# libraries are only present for OCaml compatibility. While OCaml compatibility is a key goal of the language, we have also become aware that users coming to F# from a non-OCaml background get very confused by the presence of 'multiple ways of doing things' and are bemused when the explanation is that 'we need that for OCaml compatibility, but we don't really recommend using it'. As such, we've begun marking some constructs as 'not recommended for use unless you need it for OCaml compativility.' In this relesae this applies to &
Null no longer used as a representation for F# list values. We made this change to allow the F# "List" type to implement the IEnumerable/seq interface. This means we are dropping null as a representation for F# list values (null values cannot implement interfaces), and are indeed abandoning the use of "null" for option values and where an attribute is explicitly attached to a discriminated union with a nullary discriminator, e.g.
[<CompilationRepresentation(CompilationRepresentationFlags.PermitNull)>] type HashChain<'a,'b> = | EmptyChain // note: representation is "null" | Chain of { Key: 'a; Value: 'b; mutable Rest: ('a,'b) HashChain }
This is thus a breaking change to the language spec, which previously specified different conditions where "null" is used. Using non-null values for the empty list comes with a small performance penalty for list-intensive code (around 10%), but little overall space penalty. Array, loop, matrix, vector, hashtable, set, sequence and map-dominated code is not affected, and since these are the primary high-performance data structures in F# we have decided that the language clarity of having "list" implement "IEnumerable" is more important than the performance considerations for this case.
This change does mean that C# samples that search for "null" as the empty list will no longer function correctly. They should instead use the IsNil property (now a true instance property) or switch on tags, e.g. as follows:
List<int> x = List>int>.Cons(3, (List<int>.Nil)); switch (x.Tag) { case List<int>.tag_Cons: Console.WriteLine("Cons({0},{1})", x.Head, x.Tail); break; case List<int>.tag_Nil: Console.WriteLine("[]"); break; }
Representation of the LazyList type is now private. Active patterns can now be used to match on values of type LazyList.
Sparse matrix implementation. See Math.Matrix.init_sparse. You can now create and multiply matrices up to size ~10M x 10M, as long as most elements are zero.
Updated quotations API. The quotations API has been substantially simplified through the use of active patterns. Code using the old quotations API directly will no longer compile but is easy to update. Contact the F# team if you need help you with this.
The F# map type now supports equality and comparison over entire map values. In particular Map<k,v> implements IDictionary<'k,'v>, IEnumerable<KeyValuePair<k,v>> and IComparable. Note: this is a breaking change. In 1.1.13.8 Map supported IEnumerable<'k,'v>. However, this meant that the type could never also support IDictionary. We have decided to make this breeaking change immediately since it is obviously correct for Map values to support IDictionary.
Interactive pretty-printer for IDictionary values.
Pervasive values cleaned up. Many values that are now "officially" part of Microsoft.FSharp.Core.Operators were still being redefined in Microsoft.FSharp.Compatibility.OCaml.Pervasives. These have now been removed from that module. The path Microsoft.FSharp.Compatibility.OCaml.Pervasives is still opened be default but contains very few values.
Overloading of math operations like sin, cos, abs etc. These are now overloaded over basic types, e.g. abs on all floating point and signed types, and sin on the floating point types. They also work with any type supporting corresponding static members, e.g. types supporting the static member Sin or Abs.
Choice types.
Choice types up to size 7 are defined in the F# library and can be used for general purpose programming.
type Choice<'a,'b> = | Choice2_1 of 'a | Choice2_2 of 'b type Choice<'a,'b,'c> = | Choice3_1 of 'a | Choice3_2 of 'b | Choice3_3 of 'c
etc.
F# Reflection library additions to pre-compute faster accessors. The following functions have been added to thee reflection library:
/// Precompute a function for reading a particular field from a record. val GetRecordFieldReader : Type * field:string -> (obj -> obj) /// Precompute a function for reading all the fields from a record. val GetRecordReader : Type -> (obj -> obj array) /// Precompute a function for reading an integer representing the discriminant tag of a sum type. val GetSumTagReader : Type -> (obj -> int) /// Precompute a function for reading all the fields for a particular discriminant tag of a sum type val GetSumRecordReader : Type * int -> (obj -> obj array) /// Precompute a function for reading the values of a particular tuple type val GetTupleReader : Type -> (obj -> obj array) /// Precompute a function for constructing a record value. val GetRecordConstructor : Type -> (obj array -> obj) /// Precompute a function for constructing a discriminated union value for a particular tag. val GetSumConstructor : Type * tag:int -> (obj array -> obj) /// Precompute a function for reading the values of a particular tuple type val GetTupleConstructor : Type -> (obj array -> obj) /// Precompute a pair of functions for converting between integer discriminant tags /// the names of the discriminants for the given sum type. The number of tags is also /// returned. val GetSumTagConverters : Type -> int * (int -> string) * (string -> int)
F# Reflection library no longer reports F# class types as records. This was a mistake from the early implementation of the reflection library.
Deprecated llist functions. Deprecated some LazyList functions.
LexBuffer.AsNewLinePos() renamed to LexBuffer.NextLine. The old name has been deprecated.
List.concat now accepts a sequence. This brings it into line with Seq.concat.
Notation for complex/matrix/vector/set/dict notation at the top level The following functions are now defined in Microsoft.FSharp.Core.Pervasives and are available for use in the top level:
val complex : float -> float -> complex val matrix : #seq< #seq<float> > -> matrix val vector : #seq<float> -> vector val rowvec : #seq<float> -> rowvec val set : #seq<'a> -> Set<'a> val dict : #seq<'a * 'b> -> System.Collections.Generic.IDictionary <'a,'b>
Intelisense on more expressions, including method calls.
Visual Studio ToolsOptionsPage for FSI startup arguments
Visual Studio fixes to allow "ReadLine" calls when using VFSI
Bug fixes to permit printf on binary channels (Reported by Richard Mortier - thanks Mort!) Fixed mod_float to be ocaml compatible (reported by Andrew Phillips - thanks Andrew!) Bug 858: Function types not erased from type constraints on typars of methods or types. Bug 856: no more inlining of protected calls, e.g. out of class defn. Bug 851. Removed ``0 from non-generic member names in xmldoc. Fix base calls to F# abstract members (reported by Ralf Herbrich and Phil Trelford - thanks guys!) XMLDOC changes (suggested by Andyman - thanks Andy) Permit access to nested generic classes (reported by Claudio Russo - thanks Claudio!) Fixed generation of unverifiable code when using the "property bag" syntax with properties of type 'object' and values that are value types, a bug reported by Julien Ortin Bug 880: Type definition bug (reported by Laurent on hubfs - thanks Laurent!) Bug 881: fsi needs a way to specify command line arguments (reported by Dmitry - thanks Dmitry!) Bug 882: StartupCode and PrivateImplemntationDetails types should be private (reported by Robert Pickering - thanks Robert!) Bug 883: Add check for phantom and permuting type abbreviations causing potential problems in type inference Bug 884: Instrumented profiling doesn't work on XP or Vista Bug 889: Excessive 'base' variable usage checks (reported by Tomas Petricek - thanks Tomas!)
Range Comprehensions. e.g. {1 .. 3} and {0 .. 2 .. 10}. The both are IEnumerable<int>, generating [1;2;3] and [0;2;4;6;8;10] respectively, though the operator is overloaded and can be used with other types such as characters, bigint etc. Uses of this syntax correspond to overloaded oeprators (..) and (.. ..) respectively.
IEnumerable, Array and List Comprehensions.
It's been a long time in coming, but yes, finally a version of ML with comprehension syntax built-in right into the language. But what are comprehensions? We all know that aggregate operators are a very powerful way of working with lists, maps, sets and IEnumerable values. However, an even more convenient syntax is also provided for specifying the class of IEnumerable values that can be built using operations such as choose, map, filter, concat. This syntax is compiled down to the use of the aggregate operators above and is called the comprehension syntax. It can also be used to specify the shapes of lists and arrays.
Comprehensions using for
The simplest form of a comprehension is { for ... in ... -> result }, where -> should be read as 'yield'. This is simply a shorthand way of writing IEnumerable.map. As with range comprehensions the parentheses are not optional when using this syntax to generate IEnumerable values. For example, we can generated an enumeration of numbers and their squares as follows:
> let squares = { for i in 0 .. 10 -> i,i*i} ;; val squares : IEnumerable<int * int> = [ (0,0); (1,1); (2,4); (3,9); ... ]
The more complete form of this construct is (for pattern in enumerable -> expression). The pattern allows us to decompose the values yielded by the input enumerable. For example, if the enumerable yields tuples we can decompose it as follows:
> let cubes = { for i,isquared in squares -> i,isquared,i*isquared };; val range : IEnumerable<int> = [ (0,0,0); (1,1,1); (2,4,8); (3,9,27); ... ]
The input enumerable may be a list value, any IEnumerable, or a value of any type supporting a GetEnumerator method (some important types from the .NET libraries support this method without directly supporting the IEnumerable construct). Below is an example where the input is a list of options. If the pattern fails to match the element of the enumerable is skipped and no result is yielded.
> { for Some(nm) in [ Some("James"); None; Some("John") ] -> nm.Length };; val it : IEnumerable<int> = [ 5; 4 ]
An enumerable comprehension always begins with for … in …, but additional clauses can also be given, each of which may be one of:
Secondary iterations iterate in two dimensions. For example, the following function returns all the (row,column) pairs of the lower left triangle of a square matrix, including the diagonal:
let lowerTriangleCoordinates n = { for row in 0 .. n for col in 0 .. row -> (row,col) }
Filters allow us to skip elements, e.g. the following computes the coordinates of a matrix where the sum is even (i.e. a checkerboard):
let checkerboardCoordinates n = { for row in 0 .. n for col in 0 .. n when row+col % 2 = 0 -> (row,col) }
Let and match clauses in comprehensions allow us to compute intermediary results and filter results through failing matches. For example, the following code gets the creation time and last access time for each file in a directory.
let fileInfo dir = { for file in Directory.GetFiles(dir) let creationTime = File.GetCreationTime(file) let lastAccessTime = File.GetLastAccessTime(file) -> (file,creationTime,lastAccessTime) }
The final yield of a comprehension may be another IEnumerable, signified through the use of the ->> symbol as the yield. The following sample shows how to redefine the allFiles function from the previous section using comprehension syntax.
let rec allFiles dir = IEnumerable.append (for file in Directory.GetFiles(dir) -> file) (for subdir in Directory.GetDirectories subdir ->> allFiles subdir)
Using Comprehension Syntax to Specify Lists and Arrays The comprehension syntax for ranges and generated enumerables can also be used to build list and array values. The syntax is identical except the surrounding parentheses are replaced by the usual [ ] for lists and [| |] for arrays. We discuss arrays in more detail in Chapter 3.
> let squaresList = [ for i in 0 .. 3 -> i,i*i ];; val squaresList : (int * int) list = [ (0,0); (1,1); (2,4); (3,9) ] > let squaresArray = [| for i in 0 .. 3 -> i,i*i |];; val squaresArray : (int * int) list = [ (0,0); (1,1); (2,4); (3,9) ]
Named arguments and Property-setters-masquerading-as-named-arguments
.Named items in object-model member calls are arguments of the form id=expr. Named items have been used for some time in the attribute syntax. These are now given two meanings in constructor calls and regular member calls:
new MenuItem(text="Hello") -- this is a named argument new Form(Width=300) -- this looks like a named argument, and feels like one, but -- is actually a "property set" performed after the object has been constructed.
We've been planning this feature for a while, and it is also very close to the C# 3.0 syntax for property setters. Example:
open System open System.IO open System.Text open System.Drawing open System.Windows.Forms let clickMenuItem = new MenuItem(text = "hello",onClick=new EventHandler(fun _ evArgs -> printf "click!\n")) let clickMenuItem2 = new MenuItem(text = "hello",onClick=(fun _ evArgs -> printf "click!\n")) let rec recursiveClickMenuItem = new MenuItem(text = "hello",onClick=(fun _ evArgs -> printf "self disabling\n"; recursiveClickMenuItem.Enabled <- false)) let form = new Form(Width=500, Height=420,Visible=true,TopMost=true, FormBorderStyle=FormBorderStyle.FixedSingle, Text="99 bottles of beer presented in F#" ) let rb = new RichTextBox(Size=new Size(480, 350), Location=new Point(5, 5)) form.Controls.Add(rb) let btnClose = new Button(Location=new Point(408, 360),Text = "Close") btnClose.Click.Add (fun _ -> f4.Close()) f4.Controls.Add(btnClose)
Named arguments cannot be used with let-bound functions.
Fast 'for' loops for Range Comprehensions. Constructs of the form for i in n .. m do ... done have been optimized to ensure they also result in for-loops that are compiled and execute as efficiently as the 'simple' integer for-loops for i = n to m do ... done
Custom Attributes on parameters.
Easier object construction. In particular, then blocks in constructors may now reference the variables bound prior to the object construction..
override now required when implementing abstract and virtual members..
Structs and Enums implemented. See the informal language specification.
New switch --generate-filter-blocks. Generate filter blocks to allow first-chance exception catching for non-matching exceptions. Not on by default because some CLI implementations have problems with filter blocks.
C-style syntax for native functions. Writing PInvoke signatures in F# has been a little awkward since you have to reformat the signature in entirely tedious and boring ways. You can now just use the C syntax for a native signature, e.g.
/// LAPACK/BLAS primitive matrix/matrix multiply routine [<DllImport(@"blas.dll",EntryPoint="dgemm_")>] extern void DoubleMatrixMultiply_(char* transa, char* transb, int* m, int* n, int *k, double* alpha, double* A, int* lda,double* B, int* ldb, double* beta, double* C, int* ldc); /// C := alpha*op( A )*op( B ) + beta*C let DoubleMatrixMultiply trans alpha (A: FortranMatrix<double>) (B: FortranMatrix<double>) beta (C: FortranMatrix<double>) = // Mutable is needed because F# only lets you take pointers to mutable values let mutable trans = trans // nb. unchanged on exit let mutable beta = beta let mutable alpha = alpha let mutable m = A.NumCols let mutable n = B.NumRows let mutable k = A.NumRows let mutable lda = A.NumCols // Call the BLAS/LAPACK routine DoubleMatrixMultiply_(&&trans, &&trans, &&m, &&n, &&k, &&alpha, A.Ptr, &&lda, B.Ptr, &&k, &&beta, C.Ptr, &&m)
Pattern matching on arrays. Several people have requested this OCaml feature.
Substantial tightening of #light rules. The #light syntax option was letting through many constructs that were clearly badly formatted. Extra tokens are now inserted based on indentation and column position to ensure that code conforms to the requirements of #light.
Reliable compiled names for parameters.
member syntax for object expressions. It is convenient and regular to support the member syntax used in class expressions in object expressions as well, as this allows you to move member bindings to and from object expressions without any syntactic adjustments, e.g.
{ new session with member x.Interrupt() = interrupt() member x.Input = inW member x.Output = IEvent.filter nonNull outE member x.Error = IEvent.filter nonNull errE member x.Exited = exitedE member x.Alive = not proc.HasExited member x.Kill() = killProcess proc member x.Completions(s) = completions(s:string) }
A namespace can now contain a module and type of the same name. An attribute annotation is required - see the F# library for examples (e.g. math/complex.fs). Greatly simplifies F# library design and implementation.
Physical Library Reorganization. After an extensive design review the mapping of F# modules to .NET namespaces and module names has been extensively revised. We've gone to great pains to ensure that almost no F# code will actually break because of this change: Most F# code does not refer to module by their long paths, and even for the code that does we've left lots of "deprecated" warnings in place and the compiler will generally ensure that old code works and will give advice about the new location of a construct so you can update your code at your own pace.
Why did we do this? Above all because the new mapping is very easy to explain, unlike the old one. This is very important as we come to write the F# books, and to expand the F# library over time, adn to set a good standard in library design practice with F#. Furthermore this is a really important step toward clarifying which parts of the previous library are "core" F# and which parts only exist for compatibility with OCaml.
The previous library mapping sort of grew over time: the split between FSLIB and MLLIB was never very clear, and there were just a whole heap of modules cluttered under MLLib, some of them very important indeed (e.g. List and even some things that had nothing to do with OCaml such as IEnumerable). The reorganized library design is, I think you'll agree, much cleaner, and much more along the lines of the .NET design.
The namespace layout is as follows. As part of the redesign we have added an attribute that allows a module and a type to co-exist in the same namespace.
Top level namespaces:
fslib.dll: Microsoft.FSharp.Core Microsoft.FSharp.Collections Microsoft.FSharp.Compatibility Microsoft.FSharp.Control Microsoft.FSharp.Math Microsoft.FSharp.NativeInterop Microsoft.FSharp.Quotations Microsoft.FSharp.Reflection Microsoft.FSharp.Text Microsoft.FSharp.Experimental Microsoft.FSharp.Tools.FsLex Microsoft.FSharp.Tools.FsYacc mllib.dll: Microsoft.FSharp.Compatibility.OCaml
Opened by default:
Microsoft.FSharp.Core Microsoft.FSharp.Core.Operators Microsoft.FSharp.Core.LanguagePrimitives Microsoft.FSharp.Collections Microsoft.FSharp.Control Microsoft.FSharp.Text Microsoft.FSharp.Compatibility.OCaml
More detail:
Microsoft.FSharp.Core type Ref type Option module Operators module LanguagePrimitives module {Byte,SByte,Int16,UInt16,Int32,UInt32,Int64,UInt64,Float,Float32,String,Char} module Ref module Option module Enum Microsoft.FSharp.Collections type List type SetQ, MapQ, HashSetQ type LazyList module {Array,Array2,Array3} module List module IEnumerable module LazyList module Map module Set module ResizeArray module HashIdentity module ComparisonIdentity Microsoft.FSharp.Control type IEvent type IDelegateEvent type ICompatEvent type Lazy module IEvent module Lazy Microsoft.FSharp.Math type BigInt type BigNum type Complex type Matrix<_> type Vector<_> type RowVector<_> module Matrix module Matrix.Generic module Vector module Vector.Generic module Notation module BigInt module BigNum module Complex module GlobalAssociations Microsoft.FSharp.Experimental type IPattern Microsoft.FSharp.NativeInterop module NativePtr type CMatrix type FortranMatrix type NativeArray type NativeArray2 type PinnedArray type PinnedArray2 Microsoft.FSharp.Quotations module Raw module Typed Microsoft.FSharp.Text module Printf Microsoft.FSharp.Text.StructuredFormat type IFormattable type Layout module LayoutOps Microsoft.FSharp.Text.Compatibility module CompatArray module CompatMatrix Microsoft.FSharp.Compatibility.OCaml [Arg], [Big_int], [Buffer], [Bytearray], [Filename], [Hashtbl], [Lexing], [Num], [Obj], [Parsing], [Pervasives], [Printexc], [Sys]
Note that this has:
Seq as shorthand for IEnumerable. One of my friends said 'I fall asleep reading IEnumerable'. So we've introduced the type abbreviation
type 'a seq = IEnumerable<'a>
and the module Seq. The latter is identical to IEnumerable except that you can write Seq.empty instead of IEnumerable.empty(). In the long term we may deprecate the module IEnumerable, but it is not yet marked deprecated.
New library functions. truncate and pairwise added to IEnumerable/Seq, pairwise added to IEvent. The function truncate places a maximum on the number of elements returned by the sequence. Pairwise returns a sliding window on the enumerable, i.e. elements (0,1), (1,2), (2,3) etc. For events pairwise does nothing on the first firing of the event and then returns (prior,current) pairs for each subsequent firing.
Optional Safer Collections. A recurring theme in the design of collection classes (in particular immutable ones such as Map and Set) is the desire to use the type system to track information about the comparison and hashing functions involved. Until now F# offered no way to be 'strict' about tracking this information. While this lack of 'strong typing' is standard for .NET collections, many F# and OCaml programmers tend to have a higher standard, where the type system can be used to ensure that values created using one comparison ordering aren't confused with values created with another ordering. This applies especially to Set values which support important binary operations such as union and difference. As such we've now adopted an (optional) type-tagging technique to ensure that users who want to can adopt this level of safety. Existing code will continue to work as normal. However, the Microsoft.FSharp.Collections.Tagged namespace now contains types that carry an extra type parameter, related to the comparison/hash function.
The changes mean you may use types like the following:
Tagged.Set<'a,'compareTag> -- indicates a set using some kind of comparison or Tagged.Set> -- indicates a set using structural comparison (identical to Set ) or Tagged.HashSet > -- indicates a hash set using reference comparison
Here the tag variables just mark a lack of information about the comparison function.
type SetUntagged<'a> -- a set using some user-specified comparison function that we don't know anything more about type MapUntagged<'a> -- a map using some user-specified comparison function that we don't know anything more about type HashSetUntagged<'a> -- a hash set using some user-specified comparison function that we don't know anything more about
Custom tracked comparison tags can be created by defining new class to act as the tag, or by using the Set.MakeTaggged, Map.MakeTagged and Hashtbl.MakeTagged functions.
New Library Modules. ResizeArray - this module is the F# name for System.Collections.Generic.List.
Minor changes to NativeInterop namespace based on user feedback.. NativeArray.Create now called NativeArray.FromPtr. NativeTranspose members on CMatrix and FortranMatrix. New types PinnedArray and PinnedArray2.
UnverifiableAttribute. This attribute can be used to mark code that is potentially unverifiable.
Visual Studio: Goto defintition now works across projects and into the F# library.
Resources on the command line.resx resources may now be added to the F# command-line compiler and as files in the Visual Studio mode.
Cleanup of Parser and Lexer engine APIs. Old API functions deprecated. The two namespaces under Microsoft.FSharp.Tools contain the official F# APIs for using the parsing and lexing tools.
Library addition: Matrix.copy, Vector.copy, Array.copy.
Library addition: F# Immutable Map values now support the map.[i] syntax.
Library addition: Optionally safer collection types. F# Map, Set, HashSet and Hashtbl (HashMultiMap) values now use underlying SetQ etc. types that carry extra type annotation "qualifications" about the comparison and hashing functions assocaited with the collection.
Compiled names for modules now include a "Module" suffix if a RenamedModule attribute is given. Whenever you define a public module in F# code with this attribute, the compiled name of that module now has a "Module" suffix added. This is never visible from F# code unless you use reflection. This allows F# namespaces to contain a module and type of the same name, e.g. a module called List and a type called List. The rules of the F# language disambiguate between these automatically without the Module prefix ever being visible.
Deprecated Microsoft.FSharp.Idioms. Pretty much all the useful operations from this module are now in Microsoft.FSharp.Core.Operators, which is opened by default, or there are other reasonable choices, e.g. just use using instead of Idioms.using.
Removal of some long-deprecated library functions. Idioms.foreachG, List.transform etc.
Simpler OCaml-compatible Channel abstraction. in_channel and out_channel now map to TextReader/Writer, with a hidden BinaryReader/Writer. This leads to more generally usable printf and fprintf functions, and in particular removes the final dependency of the "true" F# library on the OCaml-compatible channel abstraction. This makes "printf" much easier to understand and explain, as it can be explained directly in terms of System.Console.Out.
printf and fprintf functions can now be used in conjunction with any TextWriter streams.
Properties removed from in_channel and out_channel. e.g. Stream, StreamReader, BinaryReader. Necessary in order to simplify the implementation and mapping of the channel abstraction above. Use the functions in the InChannel and OutChannel modules instead.
F#-specific functions and operator definitions removed from Pervasives and now in Microsoft.FSharp.Core.Operators.
New LAPACK sample..
Improved speed of bracket matching in Visual Studio mode..
F# library is now free of C# code. We had a few dangling bits of C# code in the library implementation, but they are now gone.
Remove renaming of F# library from static linking process. The --standalone flag used to rename Microsoft.FSharpp to FSharp, for no particularly good reason. This has been stopped.
F# console now implements TAB completion and history.
752 F# Compiler object model members have lost their argument names 751 F# Compiler type variable names and numbers are all messed up they are the inference type variable names) 724 F# Language permit accurate names on arguments, especially for OO code (reported by Robert Pickering - thanks Robert!) 715 F# Compiler RunClassConstructor XNA on xbox 360 748 F# Compiler unicode chars not supported with FSI 652 F# Samples Samples101 does not compile resources 719 F# Compiler type checking reproted to differ with fscp10.exe 706 F# Compiler custom attributes for fields 754 F# Compiler support "out" parameter attributes 756 F# Language permit attributes on parameters 765 F# Compiler System.Runtime.InteropServices.Out on argument parameters does not generate an out parameter 737 F# Compiler nested constructs (constants) lead to huge memory and time requirements 464 F# Compiler lists, arrays, format strings not reporting error at right location 658 F# Compiler Are we checking generalization conditions when implementing abstract or virtual generic methods (making type variables rigid is not enough! Must check we can generalize) 488 F# Compiler "interface method unimplemented" errors (checked at end of scope) give a range that extends over the entire file 725 F# Library stabilize LanguageServices API 767 F# Library Map type missing a get_Item property for .[] notation 766 F# Library Map module still missing to_list, of_list, to_IEnumerable etc. 774 F# Language #light permitting several obviously incorrectly formatted expression forms 771 F# VS Mode Cant right click and go to definition through to F# library 759 F# Documentation spec and examples needed for high precedence application 508 F# VS Mode Visual Studio mode does not report missing files and allows "builds" when files don't exist 661 F# VS Mode GoTo Definition doesn't work for F# library definitions 772 F# Language pattern matching on arrays requested 770 F# VS Mode we're not showing XML help for .NET enumeration values 662 F# Language Support comprehensions 763 F# VS Mode Can't right click go to defenition 776 F# Compiler uppercase member warning 778 F# Compiler F# does not support C#-style \x1F characters 782 F# Interactive lexer errors upset F# Interactive line error reporting 781 F# Compiler use of "+" with "string" types doesn't detect that only reasonable return type is "string" 780 F# Compiler reasonable indentation of a "with" record update gives a #light syntax error 779 F# Compiler try/finally on a single line gives a #light error 760 F# Compiler #light problems reported by Mort 762 F# VS Mode F# VS mode pauses when scrolling pass a bracket-matching character 736 F# Interactive AutoExpand is missing (except in the Visual Studio PlugIn version where it is mapped to TAB) 798 F# VS Mode WINEXE option not available in Visual Studo 799 F# VS Mode properties box looks kind of messed up in Visual Studio 800 F# Compiler F# exceptions should be filtering rather than catching and rethrowing (Reported by rjblack - thanks Richard!) 720 F# Tools fslex doesn't support \xHH 819 F# Language support __SOURCE_DIRECTORY__ 820 F# Library support more instances of overloaded multiplication operator on Matrix, Vector and RowVector 821 F# Library Floating point numbers such as 1.000000000001 get printed by auto-formatting as just "1" 822 F# Library Redesign the NativeInterop module based on feedback from users and recent LAPACK work 823 F# Library Add String.for_all and String.exists 827 F# Samples Add LAPACK sample to distribution 828 F# VS Mode Double check bracket matching is still working in Visual Studio mode 777 F# Compiler lowercase static member gives an error 815 F# VS Mode Type parameter contraint errors given against wrong file 812 F# Compiler Red squiggly sprinkled randomly like confetti 806 F# Compiler #light;; is not an interaction in fsi 818 F# Language static property is interpretted as a method 802 F# Library Revamp the module locations of the F# Library 833 F# Language some long name lookups of constructors and fields not being handled correctly 835 F# Library set and map values with different comparison functions are too easy to confuse 809 F# VS Mode Installer fails on VS2003 machine - reported by Jan Sacha 837 F# VS Mode another unicode bug using VFSI 836 F# Compiler Jon Harrop's for-loop-in-constructor bug 795 F# VS Mode intellisense not working in #light code 803 F# Language cannot write a comprehension binding continaining "let" on a single line in the hardwhite syntax
Disabled generation of tailcalls on Mono and .NET 1.0. A bug has been reported in Mono's implementation of tailcalls. The fscp10.exe compiler is for use on Mono and we have adjusted this to be a binary that does not itself either make use of nor generate tailcalls (except for immediately recursive functions, where tailcalls become branches). Additionally, the fslib10.dll and mllib10.dll libraries are used when running code on Mono and have been built with tailcall generation disabled. This situation will remain until Mono's implementation of tailcalls is completely robust.
Minor library additions. Added Array.tryfind
Bug fixes.
692 18/09/2006 F# Compiler FSI fails when creating delegate type 694 19/09/2006 F# Interactive delegate invoke (wrong arity?) falls over in FSI 709 20/09/2006 F# Compiler hash on arrays do not detect numberRemaining=0, stack-overflow on circular datastructures 712 25/09/2006 F# Compiler :? error with value types in expr 710 29/09/2006 F# Compiler offside1, offside2 etc. 703 29/09/2006 F# Library hashtable add throwing stack overflow exception 708 30/09/2006 F# Compiler #light interactive bug from Rob 704 30/09/2006 F# Compiler fsi --full-help generates same output as fsc --full-help 716 30/09/2006 F# Visual Studio Intellisense does not work inside ill-formed lists (thanks to Ralf Herbrich) 723 30/09/2006 F# Compiler offside problem (reported by Dennis Hamilton - thanks Dennis!) 728 30/09/2006 F# Library UInt64.to_float and UInt32.to_float sometimes return incorrect results (reported by Dave Wecker - thanks Dave!) 729 30/09/2006 F# Compiler --help and --full-help not showing anything for fsc.exe 738 02/10/2006 F# Library Hashing big integers raises an exception
High Precedence Application. A long-standing problem with the F# syntax is that method applications followed by property/field lookups such as obj.Method1(args).Method2(args).Property1.field2 have had to be written with very non-intuitive parentheses, e.g. ((obj.Method1(args)).Method2(args)).Property1.field2.
To fix this, this release incorporates a minor change in the F# syntax (with and with the #light syntax option). In particular, applications id(args) now have higher precedence (i.e. bind more tightly) than the dot-notation. This only applies when no spaces or other whitespace separate the identifier and the arguments.
--no-tailcalls now binary-format neutral. In prior versions the --no-tailcalls option was a global setting, hence requiring the use of special libraries that had tailcalls removed. Systems such as Mono now correctly support the .tail IL annotation and so a global version of this option is no longer required. The option is still supported, but will only affect the code being generated in the assembly currently being compiled and will not result in the selection of different runtime libraries.
Revert change to operator overloading made in 1.1.11.12.2 release.
The change to operator overloading in 1.1.11.12.2 was incorrect, as it disallows the use of legitimate .NET overloads. The upshot is that the basic operators now have the following signatures. These indicate that the two argument types and the return type may indeed all be different, and knowledge of the return type and the second argument type are not sufficient to resolve the overloading - a type annotation may be required to give the precise type of the first argument.
val inline (+) : ^a -> ^b -> ^c when ^a : (static member (+) : ^a * ^b -> ^c) val inline (-) : ^a -> ^b -> ^c when ^a : (static member (-) : ^a * ^b -> ^c) val inline ( * ): ^a -> ^b -> ^c when ^a : (static member ( * ) : ^a * ^b -> ^c) val inline (/) : ^a -> ^b -> ^c when ^a : (static member (/) : ^a * ^b -> ^c) val inline (%) : ^a -> ^b -> ^c when ^a : (static member (%) : ^a * ^b -> ^c) val inline (mod): ^a -> ^b -> ^c when ^a : (static member (%) : ^a * ^b -> ^c)
We are considering a design revision which would allow more restricted specifications of permitted overloads, but are currently erring on the side of generality, even at the cost of occasional additional type annotations.
Bug fixes.
700 F# Language undentation should be allowed when () and {} immediately follow an "=" 695 F# Compiler record building construct - not checking records only not classes 702 F# Compiler unbound type variable problems for inner constrained polymorphic definitions
Lightweight syntax option. Are you sick of writing in? The #light option makes the use of certain keywords such as in optional by using indentation. See the informal language specification for details, as well as the ConcurrentLife sample and the Samples101 tutorial. Enable using #light.
First experimental cut at the Active Patterns. More details to follow on Don Syme's blog.
More modules in MLLib. Modules UInt8, Int8, Int16, UInt16 have been added.
More collections in Microsoft.FSharp.Collections. The Set and Map collections are now defined as object-oriented abstractions in Microsoft.FSharp.Collections. The corresponding MLLib modules are defined in terms of these OO abstractions.
Removed deprecated Stream module from MLLib. Minor updates to the LazyList module as a result.
--gnu-style-errors flag no longer accepts spurious integer argument.
Slight modification to overloading for +,*,/,mod.
In version 1.1.11 a slight change was made to the default overloaded signature for - operator to allow the return type to be unassociated to the two input types, as is required for overloading on the System.DateTime subtraction operator. This change was mistakenly applied to other operators, resulting in situations where type inference would report unexpected inference errors for fairly straight-forward floating point code.
Bug fixes.
--- F# Compiler -R clobbers DLLs with PDBs --- F# Compiler no properties being generated for top level values 605 F# Visual Studio VFSI scroll to caret does not always show input position 643 F# Compiler internal warning when unpickling override of abstract member 655 F# Compiler interface implementing methods marked private but included in modul_typ 656 F# Compiler Runtime exception TypeLoadException method 'Specialize'... tried to implicitly override a method with weaker type parameter constraints 645 F# Debug problems setting breakpoints in inner functions (reported by Jack Palevich - thanks Jack!) 647 F# Compiler lowercase constructors are incorrectly permitted 654 F# Interactive cannot declare delegate types interactively 672 F# Visual Studio Unapplied methods should give signature of method overloads in error message 671 F# Compiler -R feature overwrites .dll file with .pdb file 670 F# Compiler protected access not permitted via member 'this' variables 668 F# Language L-R type inference with (fun x -> x * x * 1.0) 667 F# Language exceptions to carry line numbers? 663 Fix broken codegen for generalized constrained polymorphic functions 659 F# Visual Studio Intellisense does not work in a for .. to .. do line 676 F# Visual Studio Inappropriate popup 644 F# Language execution and inference for record expressions is not always left-to-right 533 F# Release proper uninstaller 517 F# Compiler fsi.exe does not process #r/#I directives for files on command line 678 F# Library delete very old obsolete functionality fro the 'Set' module 679 F# Library Add object-oriented version of immutable 'Sets' to Microsoft.FSharp.Collections 681 F# Visual Studio hardwhite intelisense 682 F# Compiler field and property names with same name - allowed 680 F# Visual Studio intelisense bugs 665 F# Language exprs ending in semi-expr sequences cause confusion when ending constructs which are themselves semi separated.
Copy-Local reference options DLLs that are not in the GAC must be copied to an application's directory prioer to execution. This is not currently well-supported by the F# Visual Studio mode. Thus the following options are now supported by the fsc.exe compiler and are especially recommended for use from Visual Studio:
-R DLL both reference the given DLL and copy it to the output directory at the end of compilation --copy-local FILE copy the given file to the output directory at the end of compilation
Note that -R = -r + --copy-local. Also these switches are not required by fsi.exe which is able to resolve and load DLLs from any location.
New Installer InstallFSharp.msi. This currently always installs to the Program Files directory.
New library module Microsoft.FSharp.MLLib.Native Helpers for native interop. See library documentation.
Minor breaking change for native pointers. The F# "'a nativeptr" is now compiled as the .NET type System.IntPtr for all 'a. .NET pointers such as "int*" are now represented by "ilsigptr<int>" types. This cange is because although .NET has a notion of a "pointer type", used in some .NET signatures, these are not "real" types, since they can't be used within generic instantiations. This is very bad for C-interop F# code, which generates generic instantiations for function and tuple values. Thus, the F# 'a nativeptr type is now always compiled as System.IntPtr, regardless of 'a (it is not type equivalent as far as F# is concerend - this is just how the type is compiled. The pseudo-type 'a ilsigptr (IL Signature Pointer) is provided if you need to call a .NET signature using a pointer type.
COMPILED and INTERACTIVE supported as standard --define in F# Interactive and compiled code This is useful as some code fragments such as Application.Run() are only needed in compiled code. Also accessing things such as resources can vary between interactive and compiled code.
Type Inference corrections for fields and records
It is implicit in the F# informal specification that record constructions should use contextual left-to-right type information to help determine the type being constructed. This is now implemented, and makes it easier to use record syntax when record member names overlap.
Types are no longer inferred from uses of class field labels alone. Previously, defining a class "C" with a value field called, say "f" meant that "f" became a scoped record label. This meant that "expr.f" would infer the type of "expr" to be "C". This is still done for _record_ labels, but is no longer done for class field labels, and instead an annotation may be needed to constrain the type of "expr" based on left-to-right type inference. A helpful warning about the deprecation of this language feature is given when this occurs. This was always meant to be the intended treatment of inference for these constructs - it was an artefact of the initial implementation that inference for record field labels was treated in this way.
Various .NET generic constraints implemented .NET generics supports a number of somewhat adhoc constraints on the structural properties of types. It is necessary for F# to support these in order to emit correct and valid generic code and make sound use of F# libraries. The syntax of the constraints is:
when 'a : struct // any struct, with the exception of Nullable when 'a : not struct // any reference type - note - this syntax is under revision when 'a : (new : unit -> 'a) // default constructor
The following F#-specific constraint has also been added:
when 'a : null // any reference type that supports null according to the F# pseudo-enforcement rules for prohibiting the use of null with F# types when default 'a : <type> // the variable will take the given value if not otherwise instantiated or generalized
Default constructors are called using the syntax:
new 'a()
Where 'a should be related to another annotation in the same definition, e.g. an argument of the enclosing function. This constructs is compiled as a call to System.Activator.CreateInstance<'a>().
Minor improvements and Bug Fixes
573 F# Compiler list equality is not tail recursive. 613 F# Library List.combine and List.split are not tail recursive (reported by Ralf Herbrich - thanks Ralf!) 615 F# Interactive FSI reflection code throws TypeLoad error for type reference within the same interaction. 602 F# Compiler name of type not reported in "member required" error 120 F# Compiler Things stored in public static fields should only be accessible via properties 495 F# Compiler SQL cannot load F# assemblies due to publically writable statics 625 F# Compiler Poor error location for error messages related to 'new' in signatures 626 F# Library Add 'generate' methods to IEnumerable. 628 F# Compiler nested ifdefs not always handled correctly (Reported by Jack Palevich - thanks Jack!) 630 F# Compiler Error writing assembly with F# 1.1.11.7 (reported by Lewis Bruck - thanks Lewis!) 631 F# Compiler System.MethodAccessException thrown by FSI.EXE (reported by Pierre Dangauthier - thanks Pierre!) 634 F# Compiler object expression limitations: let bound object expressions not generalized (reported by Greg Neverov - thanks Greg!) 640 F# Compiler record fields should be inferred from the known context type of the expression 639 F# Compiler class fields are contributing to the record field environemnt 638 F# Compiler multiple constraints on type parameters not being correctly printed 636 F# Compiler simple object expressions implementing interfaces should be allowed in "let rec" without generating an iniitalization graph warning 632 F# Debug smoother debugging needed for inner recursive functions 648 F# Library Documentation for printf codes for 64-bit integers is incorrect (reported by Richard Mortier - thanks Richard!) 650 F# Compiler incorrectly permitting the declaration of interfaces that contain fields (reported by Robert Pickering - thanks Robert!) 649 F# Compiler bug in quotation template filling for typed quotations (Reported by Tomas Petricek - thanks Tomas!) 646 F# Compiler attributes on top level values not propagating to generated static fields, e.g. ThreadStatic (reported by Robert Pickering - thanks Robert!) 645 F# Debug setting breakpoints in inner functions sort of broken (reported by Jack Palevich - thanks Jack!)
Minor improvements and Bug Fixes
-- Fix to permit the use of lambdas taking multiple tupled arguments within quotations. -- Fix integer formats 0b101001 and 0o101030 in fsi.exe -- Fix inlining of composition operator -- Add signature to 'prim-types', hiding the presence of the "Basics" module -- Fix optimization of integer keys for hash tables
F# Language: Namespaces.. Multiple namespaces fragments now permitted in a single file. Multiple fragments can also be constrained by a single signature. Multiple different files within an assembly may also contribute to the same namespace. For example:
/// Put the concrete type definition under 'Types' namespace Microsoft.FSharp.Math.Types type BigComplex = { r: bignum; i: bignum } /// Now add a type abbreviation and module under the main namespace namespace Microsoft.FSharp.Math type bigcomplex = Microsoft.FSharp.Math.Types.BigComplex module BigComplex = begin open Microsoft.FSharp.Math.Types let complex r i = { new BigComplex with r=r;i=i } end
F# Language: Use of null values no longer require type annotations. null values previously required immediate type information. Instead, null constraints have now been incorporated into the inference process, meaning that the type associated with the use of the null value must simply be resolved at some point in the type inference scope. This means far fewer type annotations are needed when passing 'null' values to .NET.
F# Language/Library: Implementation of Structural comparison, equality and hashing now in F# code (hence easier to understand and debug). See the file prim-types.fs in the library for the implementation of the definition of structural and physical equality, used if all other optimization techniques fail.
F# Language: More General Type Checking Rule for Class Field Access and Setting. Accessing and setting a class field previously required the object being accessed to have precisely the type of the class containing that field. This has been liberalized so that the object can have any subtype of the class containing the field. This change may affect code that uses signatures, because the inferred type of a function may be more general. For example, given
type MChan = class val mutable sndLst : MChan list end let linkMChan src dstLst = src.sndLst <- append src.sndLst dstLst
the inferred type of linkMChan was previously
val linkMChan : MChan -> MChan list -> unit
but is now the more general
val linkMChan : #MChan -> MChan list -> unit
where as usual the # indicates that any subtype is accepted as the first argument. If no subtyping is required then a rigid type annotation may be used in the implementation:
let linkMChan (src : MChan) dstLst = src.sndLst <- append src.sndLst dstLst
F# Language: Overload Resolution when Multiple Overloaded Operators Exist. Some .NET types support multiple overloads for the same operator, which must be resolved according to a process similar to the resolution of overloaded methods. F# uses constraints to handle operator overloading, and now resolves the choice between multiple overloads using essentially the same technique as is used to resolve method overloading. For example, the following overloads now resolve correctly:
let f1 (x:DateTime) (y:TimeSpan) : DateTime = x - y let g1 (x:DateTime) (y:DateTime) : TimeSpan = x - y // Return type is also sufficient: let f2 (x:DateTime) y : DateTime = x - y let g2 (x:DateTime) y : TimeSpan = x - y // Just argument types are also sufficient: let f3 (x:DateTime) (y:TimeSpan) = x - y let g3 (x:DateTime) (y:DateTime) = x - y
F# Language: Type Qualified Disciminator Names. Ambiguity between constructors of a discriminated union can now be resolved by using the type-qualified path to the discriminator, for example:
type XY = X | Y type YZ = Y | Z let y1 = XY.Y let y2 = YZ.Y let f xy = match xy with | XY.X -> "X" | XY.Y -> "Y" let g yz = match yz with | YZ.Y -> "X" | YZ.Z -> "Y"
The same is true of record field names, though this has been the case for some time now.
New F# Samples: DirectX 3D Visualization, WebCrawl. See the 'samples' directory.
F# Library: Warnings now given that 'Stream' will be renamed. This module has been renamed 'LazyList'.
F# Library: BigNum performance improvements. Major implementation improvements and tuning the cross-over between the multipliers.
F# Library: Additional Math.Complex operations.
F# Interactive and Library: Default floating point format for generic printing now 'g10'. i.e. for print_any, output_any, etc. and F# Interactive.
F# Interactive: --codepage switch now accepted by fsi.exe. This controls the codepage used to read the input files. The switch does not yet apply to fsc.exe.
F# Library: Primitive structural comparison and hashing routines moved. These were in Microsoft.FSharp.Primitives.CompilerPrimitives but are now under Microsoft.FSharp.LanguagePrimitives. After a review of the library and as part of the re-implementation of structural comparison and hashing in F# code (see below) the primitive functions used for polymorphic recursive calls in structural comparison and hashing have been moved to a new module Microsoft.FSharp.LanguagePrimitives. These functions may have been called recursively from some user code. The section in the language specification has been updated to reflect this.
F# Compiler: Optimizations and Code Quality. Fewer locals are now produced in many situations where inlining occurs. This improves the quality of the end x86 code, allows the JIT to inline more often and reduces the size of the metadata in generated assemblies.
Extensible expr.[idx] syntax for string, array, dictionary and other access operations.. The syntax expr.[idx] is now shorthand for accessing the Item property on a type. This means that expr.[idx] can be used to perform lookups on essentially any .NET collection type.
Note: As with overloaded operators on integers, the types string and the array types do not in reality support Item properties in the underlying .NET metadata. However F# arranges things to give the appearance that they do.
F# for Visual Studio: Minor improvements
F# Interactive: Suffixes .fsx and .fsscript now accepted.. These are useful for F# Interactive scripts.
F# Interactive: Formatting options may now be specified. There is an fsi object, of type InteractiveSession, available in the top-level.
namespace Microsoft.FSharp.Compiler.Interactive type InteractiveSession with member FloatingPointFormat: string with get,set member FormatProvider: System.IFormatProvider with get,set member PrintWidth : int with get,set member PrintDepth : int with get,set member PrintLength : int with get,set member ShowProperties : bool with get,set member ShowIEnumerable: bool with get,set member PrintIntercepts: (StructuredFormat.IEnvironment -> obj -> StructuredFormat.Layout option) list with get,set member AddPrinter: ('a -> string) -> unit ... end
Here's an example of it's use:
> fsi;; > fsi.FloatingPointFormat <- "%.3";; > fsi.PrintWidth <- 80;; > fsi.AddPrinter(fun (x:System.DateTime) -> sprintf "%Ld" x.Ticks);; > System.DateTime.Now;; > fsi.ShowIEnumerable <- false;; > fsi.ShowProperties <- false;;
F# Compiler: The --base-address flag. Base addresses indicate the default loading address for DLLs within a memory space. Loading a DLL at a default location can reduce the need for 'fixups' that occur when a native DLL gets relocated. Native DLLs occur with F# code if you use NGEN with your F# DLLs, and it is recommended that you use an appropriate base address if rebasing conflicts occur when using NGEN. Various tools and debuggers are available on the web to help determine if rebasing is occuring.
F# Compiler and F# Interactive: Use F# with Microsoft internal or self-built versions of the CLI. Some Microsoft internal or self-built implementations of the CLI have unusual, non-standard names such as v2.0.x86chk. The --cli-version flag can now be used to specify such a version.
F# Interactive: Minor improvements
Minor improvements and Bug Fixes
574 F# Compiler issue with top level mutables (fsi.exe), reported by Andrew Fitzgibbon 389 F# Perf Printf implementation allocated too many closures 594 F# Compiler tyvar lookup failed in ilreflect 595 F# Interactive F# Interactive code generation bug when closures used inside interface implementations 596 F# Interactive F# Interactive code generation bug: Implementing generic interfaces does not always correctly work around Reflection.Emit limitations 586 F# Compiler local mutables spilled into other locals prior to use 588 F# Compiler expr of constructor argument is not typed-expr 587 F# Compiler poor error message on for i = 0 to 10 do .. done expr 590 F# Compiler match exprA,exprB with .... allocates tuples 592 F# Compiler poor error message when a constructor is too generic 582 F# Compiler fsi prints types of the latest interaction with full path 581 F# Compiler Problem with pickling: Quotation <@ 1.3 @> gives NaN 576 F# Perf Reduce number of generated IL locals 566 F# Compiler interface inheritance/extension not being printed correctly 471 F# Compiler Eliminate unnecessary .cctors --- Fixes to printing and Array2 module for non-zero-bounded multi-dimensional arrays. --- Instance members now permitted on types that may use null as a representation. They are compiled as static members. --- Fields in super classes not accessible (reported by Ralf Herbrich - thanks Ralf!)
The 'a nativeptr type. The unverifiable type constructor nativeptr is now supported for C/C++/C# pointer types, e.g. sbyte* becomes sbyte nativeptr. In principle this is a breaking change, as previously .NET pointer types were mapped to the F# type nativeint, and are now mapped to nativeptr instead. The new behaviour is, however, consistent with .NET and necessary to permit methods that overload by pointer type (e.g. the unverifiable System.String constructors accepting byte pointers) to be resolved correctly.
Minor improvements and Bug Fixes
F# Interactive for Visual Studio. It rocks! See the section above.
F# website now at http://research.microsoft.com/fsharp
Unified, first-class, composable events. .NET events can now be accessed directly through their real name (e.g. form.Click) rather than through their helper functions (form.add_Click etc.). They can also be used in a first-class way, i.e. form.Click can be passed around as a value. The type of such a value is Idioms.IDelegateEvent, which extends Idioms.IEvent. F# published delgate types should have type Idioms.IHandlerEvent. An MLLib module Microsoft.FSharp.MLLib.IEvent includes some basic polymorphic operations over IEvent types.
Quotation processing over raw quoted expressions. New quotation operators such as <@@ @@> are supported for quoting terms (expressions) in their raw form. More details can be found in the documentation on the Quotations module and the F# informal language specification.
Removed Drop-Down VS Discrimination Menus. Temporarily disabled discrimination drop-down Intellisense in VisualStudio, due to poor completions.
Minor improvements and Bug Fixes
Minor improvements and Bug Fixes
Minor syntax extension related to struct. The syntax module X = begin ... end can now be used in addition to the OCaml-compatible syntax module X = struct ... end. The use of the word struct is potentially ver confusing to .NET-savvy newcomers, who will confuse this with a C# struct. Likewise module X : begin ... end is now the recommended way of writing module X : sig ... end in signatures.
Address-of operator. Occasionally a need arises to pass a byref parameter on to a function. This can now by done using the '&expr' address-of operator. A warning will be given whenever this is used.
Source cleanup. Various cleanup work items completed.
ExportAs deprecated. ExportAs was an old, somewhat half-baked technique to export functions as classes. Now that the object model has been implemented it is no longer needed.
Matrix library redesign and cleanup. Microsoft.FSharp.Math has been reorganised.
Expression quotation library redesign and cleanup. Now called Microsoft.FSharp.Quotations.
HTML library manual pages now included in the release and on the Microsoft Research website.
HTML documentation generation with fsc.exe. Look for the --html* flags in the advanced flags. Documentation is given using "///" comment markers prior to modules, types, values and exceptions.
Fix some problems with the Visual Studio mode. Some files were missing.
Expression Quotation. Microsoft.FSharp.Experimental.Lifted (NOTE later renamed to Now called Microsoft.FSharp.Quotations) contains a range of functionality related to "lifted expressions", i.e. expression quotation, which is a form of meta-programming. This is still under development, and some important functionality is missing or incomplete, but is extensively used in the LINQ sample that is also available in the release.
LINQ Sample. The sample samples\fsharp\FLinq shows how F# can work with the Language-Integrated-Query libraries currently under development at Microsoft.
Source Release. source/... now includes the source code to the Abstract IL, ILX, the F# compiler, F# interactive, the library and the tools, as long promised.
Compiling: To compile it up you us the Makefile and the F# compiler from the distribution itself, and you currently need .NET 2.0, and there is some C/C++ helper code to compile which requires the use of the Microsoft Visual Studio 2005 C/C++ compiler. However you do not really need to compile the C/C++ code, which only enables debugging support etc. (and indeed you can reuse the absilsuppc.dll from the F# distribution itself instead of compiling it up afresh). It should be fairly straightforward to compile with .NET 1.0/1.1 (--cli-version 1.0, change define GENERICS to NOGENERICS etc.) and should also be possible to compile on Mono using 'fscp10.exe', the portable bootstrapped compiler.
General comment: The source has its many quirks and there are many things in we will be cleaing up - if it's ugly then please be merciful and ignore it. Anyway, have fun with it!
Visual Studio mode: (The source for the VS plugin is not yet included, though a crucial file 'fsharp/vs/service.ml' is - this has the key functionality that implements the line-by-line lexing, typechecking cache etc.)
Argument name annotations for documentation purpose in signatures. Arguments can be labelled for documentation purposes, in signature files (.mli/.fsi) only. Note this is not the same os OCaml labelled arguments (which permit labels to be used at the callsite). For example:
val open_in_gen: flags:open_flag list -> permissions:int -> filename:string -> in_channel
First class uses of the 'unit' type now compiled to Microsoft.FSharp.Unit. Previous versions of F# compiled the unit type to System.Object. This has been changed to give better fidelity for runtime types, especially of function values. Note this will break C# 2.0 code that has not been using delegates of type System.Action as the way of creating function types.
Library Updates.
1.
Adjust the
signature of Idioms.using.
The current signature of
Idioms.using is
val
using: (_ :> System.IDisposable) -> (unit -> 'a) ->
'a
giving somewhat awkward usage
patterns such as
let
ts = new TransactionScope()
in
using ts (fun () ->
<do something with
ts>
)
In this release the signature
becomes
val
using: ('a :> System.IDisposable) -> ('a -> 'b) ->
'b
giving considerably more C#-like
usage patterns such as
using (new
TransactionScope()) (fun
ts ->
<do something with
ts>
)
When translating code from C# we
have found ourselves defining a new version of “using” to match the above, so it
seems preferable, and something we should switch to sooner rather than
later.
2.
Standardize on
the frequent use of “|>”
The operators “|>” and “>>”
are already defined in MLLib.Pervasives as follows:
let
(|>) x f = f x
let
(>>) f g x = g (f x)
Over time it has become clear that
use of these operators, and the “|>” operator in particular, is key to
succinct expression of functional programming when using left-to-right type
inference to resolve some name access base on type information (the technique F#
uses to resolve method overloading and property accesses). Thus we will be using
this operator far more extensively in F# code, tutorials and samples, and will
assume that it’s use one of the first things an F# programmer learns.
For example, note how “a” and “ty”
do not require type annotations in the following code to resolve the use of the
“.” notation.
This is because type information is propagated from the result
type of GetAssemblies through to these binding
sites.
let allMembers =
System.AppDomain.CurrentDomain.GetAssemblies()
|> Array.to_list
|> List.map (fun a -> a.GetTypes()) |> Array.concat
|> Array.to_list
|> List.map (fun ty -> ty.GetMembers()) |> Array.concat;;
(Now write that program in any other
.NET language in 4 lines!)
Assuming familiarity with “|>”has
some ramifications for library design, leading to some of the changes. In
particular:
·
Delete ‘transform’, ‘foreach’ etc. in MLLib.List, MLLib.Array
etc.
These functions were added in an attempt to give left-to-right type
inference, in the style above. However, they were never terribly effective at
doing this.
Furthermore the use of the name “foreach” clobbered the
corresponding name in Idioms. They are best
forgotten.
3.
Include decent
support for IEnumerable-related functions
·
Add the following modules to MLLib (NOTE: not all functions are supproted in this release)
module IEnumerable
// The following functions work over the
System.Collections.Generic.IEnumerable type
val length : #IEnumerable<'a> ->
int
val hd : #IEnumerable<'a> ->
'a
val tl : #IEnumerable<'a> ->
IEnumerable<'a>
val nth : #IEnumerable<'a> -> int
->
'a
val nonempty: #IEnumerable<'a> ->
bool
val empty : unit -> IEnumerable<'a>
val init : (int -> 'a
option) ->
IEnumerable<'a>
val unfold : ('b -> ('a
* 'b) option) -> 'b
->
IEnumerable<'a>
val append : #IEnumerable<'a> ->
#IEnumerable<'a> ->
IEnumerable<'a>
val concat : #IEnumerable< #IEnumerable<'a> > ->
IEnumerable<'a>
val exists : ('a ->
bool)
->
#IEnumerable<'a> ->
bool
val for_all : ('a ->
bool)
->
#IEnumerable<'a> ->
bool
val filter : ('a ->
bool)
->
#IEnumerable<'a> ->
IEnumerable<'a>
val choose : ('a -> 'b
option) ->
#IEnumerable<'a> ->
IEnumerable<'b>
val first : ('a -> 'b
option) ->
#IEnumerable<'a> -> 'b
option
val find : ('a ->
bool)
->
#IEnumerable<'a> ->
'a
val tryfind : ('a ->
bool)
->
#IEnumerable<'a> -> 'a
option
val iter : ('a ->
unit)
->
#IEnumerable<'a> ->
unit
val iteri : (int -> 'a
->
unit) ->
#IEnumerable<'a> ->
unit
val fold : ('b -> 'a
-> 'b)
-> 'b
->
#IEnumerable<'a> ->
'b
val map : ('a -> 'b)
->
#IEnumerable<'a> ->
IEnumerable<'b>
val mapi : (int -> 'a
->
'b)
->
#IEnumerable<'a> ->
IEnumerable<'b>
val of_array: 'a array -> IEnumerable<'a>
val of_list : 'a list -> IEnumerable<'a>
val to_array: #IEnumerable<'a> -> 'a
array
val to_list : #IEnumerable<'a> -> 'a
list
// The following functions work over the
System.Collections.IEnumerable type
// and are available on .NET
1.0/1.1
val untyped_fold : ('b -> 'a -> 'b) -> 'b -> #IEnumerable -> 'b
val untyped_iter : ('a -> unit) -> #IEnumerable -> unit
val untyped_map : ('a -> 'b)
-> #IEnumerable -> IEnumerable
val untyped_filter: ('a -> bool) -> #IEnumerable -> IEnumerable
val untyped_to_list:
#IEnumerable -> 'a list
·
Mark the following as obsolete.
Idioms.foldeach (replaced by
IEnumerable.fold_untyped)
Idioms.transform (replaced by
IEnumerable.map_untyped)
Idioms.foldeachG (replaced by
IEnumerable.fold)
Idioms.transformG (replaced by
IEnumerable.map)
Idioms.foreachE (never needed – all relevant
types implement IEnumerable)
Idioms.foldeachE (never needed – all relevant
types implement IEnumerable)
Idioms.transformE (never needed – all relevant
types implement IEnumerable)
Idioms.foreachEG (never needed – all relevant
types implement IEnumerable)
Idioms.foldeachEG (never needed – all relevant
types implement IEnumerable)
Idioms.transformEG (never needed – all relevant
types implement IEnumerable)
This leaves the
following in Idioms:
val foreach :
#System.Collections.IEnumerable
-> ('a
-> unit)
->
unit
val foreachG:
#System.Collections.Generic.IEnumerable<'a> -> ('a -> unit) -> unit
Rationale: The
purpose of Idioms is to hold F# representations of idioms in .NET, C# and other
languages. It should be comprehensible to beginner F# users. The above
functions were added in a bit of a hurry and simply shouldn’t be in Idioms,
since their purpose is to offer a consistent set of folding and mapping
operations over both the generic and non-generic (untyped) IEnumerable and
IEnumerator types. Much preferable is a module in MLLib that holds these
operations using the consistent MLLib naming scheme (iter, fold, map etc.).
Note: the LINQ
initiative will offer further platform-standard functionality similar to the
above, and when the final version of LINQ is released we will support a
namespace such as Microsoft.FSharp.Bindings.Linq which maps the Linq operators
into F#. Having a half-baked version of this stuff in Idioms doesn’t do anyone
any good. However we can’t wait for the release of Linq to fix this sort of
thing, and in any case Linq uses naming conventions which are different to F#
(select for map, where for filter etc.).
·
Mark as obsolete the very thinly
populated and undocumented
Idioms.IEnumerable, Idioms.IEnumerator. These were again added in a
hurry leading up to the last release and are again best
forgotten.
4.
Include
consistently named support for Enum-related functions
·
Add the following module to MLLib
module Microsoft.FSharp.MLLib.Enum
///Convert an enumeration value to an
integer.
The argument type is inferred from
context.
val to_int: 'a ->
int
when 'a
:> System.Enum
///Convert an integer to an enumeration
value.
The result type is inferred from context.
val of_int: int ->
'a
when 'a
:> System.Enum
///Combine enum values using 'logical
or'. The relevant enumeration type is inferred from
context.
val combine: 'a list -> 'a
when 'a
:> System.Enum
///Test if an enumeration value has a
particular flag set, using 'logical and'.
///The relevant enumeration type is
inferred from context.
val test: 'a -> 'a
->
bool
when 'a
:> System.Enum
·
Mark the following as obsolete.
Idioms.EnumToInt (replaced by
Enum.to_int)
Idioms.IntToEnum
(replaced by
Enum.of_int)
Idioms.CombineEnumFlags (replaced by
Enum.combine)
Idioms.TestEnumFlag (replaced by
Enum.test)
Rationale: The
purpose of Idioms is to hold F# representations of idioms in .NET, C# and other
languages. The above functions are not particularly idioms in any other .NET
language – they just represent things where you have to be a bit more explicit
in F#. They date from the day when Idioms was a sink for “anything which you
needed to do with a runtime check”. Their naming is not particularly consistent
with any other style. Nor are they particularly easy to
find.
It is much preferable to add a
module to MLLib that holds these operations using the consistent MLLib naming
scheme (to_int, of_int etc.).
Note: A future
version of F# may support + etc. on enum types.
Bug Fixes. Various minor fixes.
511 F# Interactive 1 0 errors raised during optimization exit fsi.exe 522 F# Interactive 1 1 fsi bug with operator names 523 F# Interactive 1 0 Automatic binding to a relative path (or the current directory) fails 532 F# VS Plugin 1 1 VS -I and -r relative paths are relative to somewhat random working directoy of VS process rather than the project/file working directory 534 F# VS Plugin 2 1 off-by-one error in VS mode balloon tips 525 F# VS Plugin 1 1 visual studio prior inputs with unicode signature are not being parsed correctly 537 F# Docs 1 1 Abbreviated types appearing in XMLDoc files, also 'unit' 543 F# Language 1 1 printing "List" and "Option" for types generated by uses of constructors looks gross 433 F# Compiler 2 2 Represent "unit" as a real type rather than abbreviating to "obj" 453 F# Compiler 2 2 print_any - no printing of repeated terms - no print depth/width controls 505 F# Language 1 0 implement a way to name arguments - crucial documentation 529 F# Compiler 1 0 differing orders of fields for records in signatures and implementations cause problems for record constructors and "with" 549 F# VS Plugin 1 0 VS redirecting stdout to Output window interferes with stdin 552 F# VS Plugin 1 1 VS mode show 2 resolutions for .NET types 553 F# VS Plugin 1 1 VS Mode method tips bounce back to first entry every time the "down" key is pressed (reported by Artem - thanks Artem!) 554 F# VS Plugin 1 1 Balloon Tips not showing XMLDoc help text for methods and types 555 F# VS Plugin 1 1 Intellisense not showing signatures and overloads for .NET constructors. 556 F# Documents 1 1 Incorrect XMLDoc signatures being generated for generic methods and some other generic gadgets 539 F# Interactive 1 0 compile F# Interactive on .NET 1.0 560 F# VS Plugin 1 1 TAB in VS mode doesn't activate completion 561 F# Language 1 1 Module abbreviations not permitted in signatures 563 Abstract IL 2 2 AbstractIL library contains duplicate hashtable modules etc. 562 F# Library 1 1 printf %s formats do not permit padding specifications 564 Abstract IL 1 1 Abstract IL library module names need to be much more sensibly organized
Matrix Library. Microsoft.FSharp.Math.Matrix<_> has been added as a generic matrix type, with extensive MATLAB-like operations at Microsoft.FSharp.Math.MatrixOps<_> and Microsoft.FSharp.Math.MatrixOps.Generic;. The former are for floating point matrices Matrix<float> = matrix, and the latter are generic over all matrix types. The interface to this library is likely to be stable. Vector and RowVector types are also defined - their status sill undergo revision after a few releases.
Very Preliminary Linear Algebra. Microsoft.FSharp.Math.LinearAlgebra<_> contains a handful of basic linear algebra functions.
Compositional, Customizable Structured Printing. A preliminary but powerful approach to user-definable structured display of terms has been implemented in this release. The solution is different to the OCaml-style "Format" library and is instead based on the generation of intermediary "layout" objects. So far, a layout describes how words will fit next to each other and where the breaks (and indentations) will/can be.
A layout engine typically constructs most layouts using a generic algorithm based on the term structure of F# data accessed via Microsoft.FSharp.Experimental.Reflection. Types that wish to customize their structured format specification can implement the StructuredFormat.IFormattable interface, which must provide a function that generates a StructuredFormat.Layout value. An environment is passed to the object that provides a function to use to generate layout for recursive calls. Leaf objects such as numbers and strings can be left unformatted, which gives a formatting engine control over how these appear (e.g. culture specific formatting of numbers). A default formatting engine is provided in Microsoft.FSharp.MLLib.Pervasives.LayoutOps for formatting layouts to strings, channels and text buffers. This engine can be customized to some extent (e.g. by print width, depth, culture and floating-point number formatting) and is used by thw following functions:
as well as the F# Interactive pretty printer. The end result is that objects are converted to textual formats consistently and in a customizable way. Here is and example of the output generated, here for a matrix holding further matrices:
> open Math;; > open MatrixOps.Generic;; > let m = matrix [[1.0;2.0;3.0]];; val m : Matrix<float> > m;; val it = matrix [[1.000000; 2.000000; 3.000000]] > let m = matrix [[m];[m];[m]];; val m : Matrix<Matrix<float> > > m;; val it = matrix [[matrix [[1.000000; 2.000000; 3.000000]]]; [matrix [[1.000000; 2.000000; 3.000000]]]; [matrix [[1.000000; 2.000000; 3.000000]]]]
Here is an example of customization of the structured layout to format complex numbers, without actually specifying the formats of the constituent components:
open StructuredFormat.LayoutOps type Complex = { real: float; imaginary: float } with member x.r = x.real member x.i = x.imaginary interface StructuredFormat.IFormattable with member x.GetLayout(env) = objL (box x.r) ++ rightL "r" ++ sepL "+" ++ (objL (box x.i) $$ rightL "i") end
Named arguments for attributes. Attribute specifications can now take named arguments, e.g.
[<DllImport("KERNEL32.DLL", EntryPoint="MoveFileW", SetLastError=true, CharSet=CharSet.Unicode, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)>] let MoveFile ((src : string), (dst: string)) : bool = failwith "extern"
Abstract properties. Interfaces and classes may now define abstract properties and specify overrides and defaults for these. This is functionality that was not completed in the first release of the object model. e.g. in an interface definition:
Abstract properties and properties in interfaces. Interfaces and classes may now define abstract properties and specify overrides and defaults for these. This is functionality that was not completed in the first release of the object model. e.g. in an interface definition:
type IEnvironment = interface /// A property indicating the maximum number of columns abstract MaxColumns : int /// A property indicating the maximum number of rows abstract MaxRows : int end
And in an implementation by a class:
type MyEnvironment = class C interface IEnvironment with method x.MaxColumns = 3 method x.MaxRows = 4 end end
And in an implementation by an object expression:
{ new IEnvironment with MaxColumns = 3 and MaxRows = 4 }
Rename 'MLLib.Vector' to 'MLLib.ReadonlyArray'. 'Vector' and 'vector' are now used for Microsoft.FSharp.Math.Vector, mutable column vectors whose element type typically support certain element operations.
Bug Fixes.
470 F# Compiler Constructors and method can't be overloaded betweeen 0/1 arguments 473 F# Compiler Operator overloading bug 474 F# Visual Studio Pattern hints for lists showing operator names, also no testing for these 475 F# Visual Studio Pressing BAR causes poor menus to pop up in Visual Studio too often 478 F# Compiler #use only processes one interaction per file 479 F# Compiler fsi - ctrl-C during command line arg processing unhandled. 481 F# Compiler fsi - syntax error recovery... 482 F# Language any_to_string doesn't print arrays 483 F# Compiler internal type variable error (reported by Karthik) 484 F# Compiler overloaded indexer properties not correctly resolved (from F# Mailing list) 485 F# Compiler uppercase non-constructor names used in patterns do not give a warning 486 F# Doc Fix documentation bug (reported by Greg Chapman - thanks Greg!) 491 F# Library Equality not implemented on matrices 492 F# Compiler imperative type vars and type var bindings not being shown in error messages (reported by Karthik) 391 F# Compiler F# not automatically usable on x64 boxes 372 F# Compiler F# gives syntax error for OCaml top level expressions 349 F# Compiler Inferred constraints of the form "x :> obj" should be ignored (e.g not required in signatures) 250 F# Visual Studio Load on-the-fly XML documentation into Visual Studio, or connect to VS interfaces that will do this for us 309 F# Compiler import C# constrained polymorphism as F# constrained polymorphism 315 F# Visual Studio output does not go to console window by default 490 F# Library Consistent approach to structured print layout needed
Bug Fixes. Various minor fixes to F# Interactive, e.g. large bytearrays. F# Interactive now works on .NET 2.0 Relase Candidate versions. Additional warnings on some minor incomplete features.
Visual Studio. Now supports "Go To Definition" and "Go To Declaration"
MLLib.Printf. Now supports '%M' for decimal values and '%O' for any 'any' object value. These are currently printed using Object.ToString() but may in the future be printed using any_to_string.
Bug Fixes. Visual Studio fixes for '|' triggers.
Core Language. Integer constants for the types 'bignum' and 'bigint' are now supported, e.g.
do Printf.printf "%O\n" 3N do Printf.printf "%O\n" 3I do Printf.printf "%O\n" (3N / 4N) do Printf.printf "%O\n" (3N / 400000000N) do Printf.printf "%O\n" (3N / 3N) do Printf.printf "%O\n" (-3N) do Printf.printf "%O\n" -3N do Printf.printf "%O\n" (-3N / -3N) do Printf.printf "%O\n" -30000000000000000000000000000000000000000000000000000000000000N
The following operartors are now supported and can be used for multi-dimensional array lookup/assignment.
arr.(i,j) -- Look up a rectangular (non-jagged) 2D array of type 'ty[,]' arr.(i,j) <- x -- Assign into a rectangular (non-jagged) 2D array of type 'ty[,]' arr.(i,j,k) -- Look up a rectangular (non-jagged) 2D array of type 'ty[,]' arr.(i,j,k) <- x -- Assign into a rectangular (non-jagged) 2D array of type 'ty[,]'
The MLLib modules Array2 and Array3 provide basic operations for 2 and 3 dimensional arrays. .NET operations on the System.Array type can also be used directly.
Library. The Compatibility.CompatArray and Compatibility.CompatMatrix module now give better results (no polymorphism restrictions apply) if inadvertantly used from .NET 2.0. Use of these modules is not recommended from .NET 2.0 but sometimes occurs when code is copied from cross-compiling samples.
Attributes. Specification clarifications for attributes. Attributes now give warnings when used inappropriately. Attributes can now be referenced with or without the Attribute suffix, e.g. [<Obsolete("this function is obsolete")>] or [<ObsoleteAttribute("this function is obsolete")>].
Compilation Speed Optimizations. Improved compilation speeds, especially when using the --standalone flag.
VS Mode. Better response as now compacts less often. Now accepts all fsc.exe flags within the argument window: those irrelevant to type-checking are still used when the command line compiler is invoked.
Minor renamings. The .NET compiled names of some exceptions have changed to follow .NET library design guidelines
AssertFailure --> AssertionFailureException.
MatchFailure --> MatchFailureException.
Undefined --> UndefinedException.
The names from F# code are now either the above names or, when using MLLib, the equivalents Assert_failure, Match_failure and Undefined.
Turning off default augmentations for discriminated unions. By default F# dsicriminated unions are augmented with properties and methods such as member IsRed : bool,member Red : int -> Color and Red1 : Color -> int for a constructor Red of int in a type Color. Now that augmenetations are supported directly in the F# language it is often more convenient to manually design these augmentations. The default augmentations can now be suppressed by using the [<DefaultAugmentation(false)>] attribute. For example, the Option<'a> type in the F# library is defined as:
[] /// The type of optional values. When used from other .NET languages the /// empty option is the 'null' value. type Option<'a> = None | Some of 'a /// Augmentation type Option<'a> with member x.Item = match x with Some x -> x | None -> op_Raise (new System.IndexOutOfRangeException()) static member IsNone(x : Option<'a>) = match x with None -> true | _ -> false static member IsSome(x : Option<'a>) = match x with Some _ -> true | _ -> false static member None : Option<'a> = None static member Some(x) : Option<'a> = Some(x) end
Initial support for Microsoft.FSharp.Experimental.Collections With the completion of the F# support for members and augmentations we are moving the functionality of the MLLib collections to be co-designed libraries for functional and object oriented programming. The means that a functional programming API to the collection types will be offfered in MLLib, and a mixed object-oriented/functional API within FSLib. This will greatly improve the usability of the collection types from C# and other .NET languages, without losing the essence of OCaml-compatible ML programming. The first class we have applied this treatment to is Microsoft.FSharp.Experimental.Collections.HashTable and the related HashSet, CHashTable and CHashSet.
Generic Recursion support temporarily withdrawn. Some bugs were found in the support for generic recursion added in 1.1.0.4 (unverifiable binaries could be produced in some situations). In particular, full signatures need to be given to make a function eligible for generic recursion, and this is not yet enforced. Functions and values may still be given explicit type parameters, but recursive uses of explicitly paramaterized values must at invariant instantiations, as was always the case for versions prior to 1.1.0.4.
Bug fixes. Several bug fixes related to attributes, generalization, enumerators and exception abbreviations, Arg.usage. Thanks to Martin Churchill, Dominic Cooney, SooHyoung Oh, Ondrej Rysavy, Robert Pickering, Greg Lee and Adam Granicz among others for reporting these bugs. Some other bugs recently recorded as fixed in out database are as follows:
443 F# Library MLLib.Stream incorrect behaviours 442 F# Compiler print_any raises exceptions on singleton constructor values 452 F# Compiler Visual Studio and fsc.exe both have problems with unicode, reported by DOminic Cooney 440 F# Tools lex/yacc - partial parses of stdin require extra token before succeeding and discard buffered input 441 F# Compiler uncaught exceptions are not reported in user friendly way (no details of what they carry...) 444 F# Compiler records compile to classes with duplicate members names - a problem for debugging 445 F# Compiler serialisation of nil list fails 426 F# Compiler "end of file in string or comment" error doesn't report start-of-string-or-comment 459 F# Compiler Undefined type variable problem 458 F# Compiler typechecker doesn't decode all attributes correctly (decode_simple_cattr_data) 371 F# Compiler Support adding managed resources to .NET DLLs and EXEs 422 F# Compiler VideoPlayer sample gives error 406 F# Library Bignums package 470 F# Compiler Constructors and method can't be overloaded betweeen 0/1 arguments 467 # Library any_to_string on char fails on 1.1
Compiler for use with Mono.
Later note: as of F# 1.1.12 when used with Mono 1.1.17 the comiler for Mono is fscp10.exe and tailcalls appear to be correctly supported by Mono.
Old note: The binary fscp10ntc is a version of the F# compiler that is reported to work to some extent in conjunction with the Mono CLI implementation. Tailcalls have reported to cause problems on Mono, hence this is a "no tailcall" (ntc) version of the compiler, which leads to changes in performance. The matching "ntc" versions of the libraries are also included. This is also a CLI 1.0 binary, again changing the performance.
This version of the compiler is reported to be sluggish, so a reasonably fast machine may be required. It may also be worth investigating the use of pre-compilation (mono --aot) in conjunction with this binary to improve startup times (pre-compilation is pretty much essential for any compiler).
The F# Object and Encapsulation Extensions. F# types can now be augmented with properties, members and operators. Furthermore, .NET-style class and interface types may also be defined in F# itself. Fairly complete documentation is provided in the language specification.
Big integers and arbitrary sized rational arithmetic. The types under Microsoft.FSharp.Math.* are the implementations of the F# arbitrary precision integer and rational arithmetic types. A partially-OCaml-compatible version of this functionality is included in MLLib, i.e. use the functionality available via 'open Num'. The types support overloaded operators. The naturals are used to build arbitrary sized integers and rationals. The implementation aims to for a lower garbage cost and provides multiplication that scales effectively up to huge numbers.
The F# Language Specification (Preliminary). A work-in-progress language specification is now included in the manual.
Specification clarifications.
Attributes attached to values in F# signatures (.fsi and .mli files) are incorporated into any F#-specific interface metadata associated with the generated assembly and the .NET IL metadata for the generated assembly. Attributes attached to values in F# implementations (.fs and .ml files) are only saved into the .NET IL metadata. Thus attributes that are relevant to F# type checking (e.g. Obsolete attributes, which generate warnings at compile time when constructs are used) must be placed in signatures. Attributes from signatures need not be duplicated in implementations.
Managed and Unmanaged Resource Linking. The command-line flags --resource, --link-resource and --win32res are now supported by the F# compiler for embedding native and managed resources (text files, icons, bitmaps, audio files etc.) into executables. They have the same meanings as the corresponding C# flags.
New in Microsoft.FSharp.Idioms. The following are new in this module of useful .NET idioms:
val foreachE: (_ :> System.Collections.IEnumerator) -> ('a -> unit) -> unit val foldeachE: (_ :> System.Collections.IEnumerator) -> 'acc -> ('acc -> 'a -> 'acc) -> 'acc val transformE: (_ :> System.Collections.IEnumerator) -> ('a -> 'b) -> System.Collections.IEnumerator val foreach: (_ :> System.Collections.IEnumerable) -> ('a -> unit) -> unit val foldeach: (_ :> System.Collections.IEnumerable) -> 'acc -> ('acc -> 'a -> 'acc) -> 'acc val transform: (_ :> System.Collections.IEnumerable) -> ('a -> 'b) -> System.Collections.IEnumerable #if GENERICS val foreachG: (_ :> System.Collections.Generic.IEnumerable<'a>) -> ('a -> unit) -> unit val foldeachG: (_ :> System.Collections.Generic.IEnumerable<'a>) -> 'acc -> ('acc -> 'a -> 'acc) -> 'acc val transformG: (_ :> System.Collections.Generic.IEnumerable<'a>) -> ('a -> 'b) -> System.Collections.Generic.IEnumerable<'b> val foreachEG: (_ :> System.Collections.Generic.IEnumerator<'a>) -> ('a -> unit) -> unit val foldeachEG: (_ :> System.Collections.Generic.IEnumerator<'a>) -> 'acc -> ('acc -> 'a -> 'acc) -> 'acc val transformEG: (_ :> System.Collections.Generic.IEnumerator<'a>) -> ('a -> 'b) -> System.Collections.Generic.IEnumerator<'b> #endif type 'a sizeof = { result: int } val inline sizeof: unit -> $a sizeof
Bugs fixed.
438 F# Compiler Mixed null and string matches bypassed pattern simplifier 441 F# Compiler Uncaught exceptions wrapping a string report their message. 442 F# Compiler Experimental.Reflection failed on singleton constructor values. F# VS Mode Type inference variables appearing in the displayed hover-text for F# values F# Library open_in now opens in ASCII encoding. Use stream_reader_to_in_channel to open in other encodings
Local names. Improved naming of locals to help during debugging.
Additional Overloading Strings now support overloaded '+'.
Customizable Debugger Views for F# Types. F# types can now be augmented in a a number of ways to customize how the values appear in a debugger. Firstly, the ToString member may be adjusted for each F# concrete type (i.e. record, discriminated union and object types). Secondly, the .NET 2.0 standard DebuggerDisplay attribute can be used in conjunction with member augmentations to customize the simple textual display associated with a type. For example:
type [] MyIntList = | MyNil | MyCons of int * MyIntList with member x.Length = let rec length x acc = match x with MyNil -> acc | MyCons(a,b) -> length b (acc+1) in length x 0 end
Finally, for sophisticated structured collections the .NET 2.0 standard DebuggerTypeProxy can be used in conjunction with member augmentations to specify a class that represents the visual display of an object. For example:
type [] MyIntList = MyNil | MyCons of int * MyIntList and MyIntListDebugView = class val v: MyIntList new(x) = { v = x } [ ] member x.Items = let rec length x acc = match x with MyNil -> acc | MyCons(a,b) -> length b (acc+1) in let len = length x.v 0 in let items = Array.zero_create len in let rec go n l = match l with MyNil -> () | MyCons(a,b) -> items.(n) <- a; go (n+1) b in go 0 x.v; items end
Custom Attribute Extensions Custom attributes now accept 'type' arguments. Double parantheses (to disambiguate the start and finish of the extra argument) and the keyword type must currently be used:
[]
Library additions: Option. The Option module is now a standard part of MLLib. It has a set of functions similar to List.
Library additions: Printf.failwithf and more. The Printf module now supports the failwithf function, which uses structured formatting to print to a string and then raise a Failure exception for this string. The new printer bsprintf is also supported: this prints to a string but intermediary functions print to string buffers. More general compositional forms of Printf operators are also included that let you specify a 'final action' for the printig such as flushing or raising an exception. Finally the OCaml-style 'format4' printer specifications are now also supported - these enable overall return types to be distinct from the types generated by intermediary printers.
Inclusion of a very preliminary release of a top-level command-line interactive environment for F#. The top-level environment will be fsi.exe. This is work in progress, and is included only for testing purposes.
Inclusion of a very preliminary release of FSharp.Compiler.dll, a hostable F# compiler. This is work in progress, and is included only for testing purposes.
Defining Events in F# Events should be defined using new definitions in Microsoft.FSharp.Idioms. For example:
open System.Windows.Forms open Idioms type MyCanvas = class inherit Form val redrawListeners: EventListenersmember x.Redraw = x.redrawListeners.Event override x.OnPaint(args) = x.redrawListeners.Fire(args) new() = { inherit Form(); redrawListeners= new EventListeners () } end let form = new MyCanvas() do form.Redraw.Add(fun args -> Printf.printf "OnRedraw\n") do form.Activate() do Application.Run(form)
Note we are using a property of type Idioms.IEvent<PaintEventArgs> to represent the event. The object returned by this property has Add, AddHandler and RemoveHandler methods (see below). In a future release of the compiler properties of type Idioms.IEvent<PaintEventArgs> will automatically result in appropriate .NET metadata for the event being inserted in the generated assembly. In the current release events defined using this mechanism may still be used from C# and other .NET by using AddHandler and other methods on themediating object.
type Idioms.SimpleEventArgs<'a> = class inherit System.EventArgs member Data: 'a new: 'a -> SimpleEventArgs<'a> end type Idioms.SimpleEventHandler<'a> = System.EventHandler> type Idioms.IEvent<'a> = interface inherit IDelegateEvent // The inheritance gives: // abstract AddHandler: SimpleEventHandler<'a> -> unit // abstract RemoveHandler: SimpleEventHandler<'a> -> unit // We add this one, which from F# code this is very simple to use: abstract Add: ('a -> unit) -> unit end type Idioms.event<'a> = IEvent<'a> type Idioms.EventListeners<'a> with member Fire: 'a -> unit member Event: IEvent<'a> new: unit -> EventListeners<'a> end
Renamed fslib10ng.dll to fslib10.dll, since there was a bug with using F# with Visual Studio 2003, and also the "ng" (non-generic) suffix was redundant and clumsy.
Stabilized string hash codes across .NET v1.1 and v2.0. That is, F#'s structural hash function no longer hashes strings by calling String.GetHashCode, since the hash codes returned were different between version 1.1 and 2.0.
Fixed a bug with the debug marks being attached for the entrypoint of executables.
A special function Pervasives.rethrow is now supported. This rethrows the exception for the current "try/with" block. However, it may only be used in catch blocks. Using it in a first class way or outside a catch block will result in a binary that cannot be verified. The correct use of this function is not checked in this version of F# but will be checked in a later version.
Fixed a bug that prevented the generic EventHandler type from being used (or indeed any generic type that was in the same namespace as a non-generic type with an identical name).
F# .EXEs that do not use function values, list types, option values or any other fslib or mllib no longer pick up a dependency on fslib.dll. DLLs incorporating interface data or optimization data still acquire a dependency.
Welcome James Margetson to the F# team!
F# now works with Whidbey Beta 2 releases of .NET (EDITOR: the latest releases no longer support Beta 2). F# can continue to be used with .NET 1.0 and 1.1, but can no longer be used with Whidbey Beta 1. If you are still using Whidbey Beta 1 and don't want to upgrade to Whidbey Beta 2 then add --cli-version 1.1 to your compilation switches to compile for .NET 1.1 (likewise 1.0) instead.
Change to Library Naming. The F# libraries fslib and mllib now come in two flavours: one for use with .NET 1.x (no generics) and one for use with .NET 2.0 Beta 2 and beyond (this is to ensure that the library can take advanatage of new features of the platform). The .NET 1.x version of the library has the suffix "10" attached. Thus you will see both fslib.dll and fslib10.dll in this release. F# will automatically reference the correct DLL. When compiling C# code with .NET 1.x you will need to reference fslib10.dll and mllib10.dll.
Rename some internal functions. Some internal functions such as GenericCompare have been renamed appropriately, e.g. to StructuralCompare.
Performance improvements.do Console.WriteLine("res = {0}\n",Decimal.op_Addition(new Decimal(10), new Decimal(10))) do Console.WriteLine("res = {0}\n",(new Decimal(10)) + (new Decimal(10))) do Console.WriteLine("res = {0}\n",(new DateTime(1970,10,1)) + (new TimeSpan(1000000000L))) do Console.WriteLine("res = {0}\n",(new DateTime(1970,10,1)) - (new TimeSpan(1000000000L))) do Console.WriteLine("res = {0}\n",(new Decimal(20)) / (new Decimal(10))) do Console.WriteLine("res = {0}\n",(new Decimal(20)) - (new Decimal(10))) do Console.WriteLine("res = {0}\n",(new Decimal(20)) * (new Decimal(10))) do Console.WriteLine("res = {0}\n",(new Decimal(20)) mod (new Decimal(7)))
Bugs fixed
351 F# Compiler Use of invalid format specifier such as %l in a Printf string gives a poor error message 381 F# Library input_char cause exception to be thrown 318 F# Compiler F# lets two constructors have the same name and gives error when emitting binary 321 F# Compiler compiler error reported by Greg Lee 332 F# Compiler F# reports error when a "for" variable is used within an inner closure, complaining it is mutable 333 F# Compiler Cannot hide exception declarations 252 F# Perf fsyacc parser is allocating a lot of list and option nodes 399 F# Perf Move generation of comparison and hash methods earlier (to typechecker) so that the code can be fully optimized (and inlined!!) 281 Abstract IL implement output of debug symbols for Abstract IL and bootstrapped compiler 175 F# Tools Implement error recovery in fsyacc generated parsers
Restricted operator overloading to built-in types only for the time being. This is simply an implementation incompleteness.
Added float32 and other float32-related operations to Pervasives.
Constrained Type Parameters
F# now allows type parameters to be constrained to specify the minimum functionality the instantiating type must support.
You may be familiar with constrained type parameters from elsewhere. For example, C# and .NET generics support 'subtype' constraints on generic type variables. OCaml supports structural 'object-type' constraints and an additional kind of variable known as a 'row' variable. Standard ML supports a two minor forms of constrained polymorphism in the form of record types (which must be locally resolved) and equality types.
Constrained polymorphism affects both type checking and type inference. In this release, F# supports coercion constraints (on any type variables) and overloaded operator constraints (on pseudo type variables).
Coercion constraints are of the form typar :> type, and also arise from the constructs expr :> type and pattern :> type. For example:
val throw: 'e -> unit when 'e :> System.ExceptionThe same declaration can be written using the following more convenient syntactic forms:
val throw: (_ :> System.Exception) -> unit
class C { static void SomeMethod(IComparable x) }Then calling this with the F# code:
C.SomeMethod(x)
will induce a constraint that x is coercable to IComparable, i.e. ty :> IComparable when ty is the static type of x.
C# 2.0 and other .NET 2.0 code may use coercion constraints to specifying that a type parameter should support functionality such as IComparable. However, this pattern is not so common in F# code, where dictionaries of functionality are typically passed around by using tuples or records of function values.
Overloaded operator constraints arise when using overloaded operators +, - etc. For example, the operator + may be used on any two .NET values supporting the overloaded operator op_Addition (written static operator +(...) in C#). (They may also be used on built-in integer and floating point types, which are considered by F# to implicitly define operators such as op_Addition). Overloaded operators are generally only defined within the F# library. Overloaded operator constraints can only be placed on pseudo type variables.
(Aside: pseudo type variables are type variables that occur only within the type checking of a single file. These type variables arise primarily from the use of pseudo-functions such as overloaded operators.)
Examples
In a signature a value declaration may be annoated with constraints. The most primitive way to do this is to use a when annotation on a value declaration. We saw an example of this above. The same declaration can be written using the following more convenient syntactic forms:
val throw: 'e -> unit when 'e :> System.Exception val throw: (_ :> System.Exception) -> unit val throw: ('e :> System.Exception) -> unit
As with types, constraints will be inferred from the definition of a method. For example
open System.IO let to_binary_writer s = new BinaryWriter(s)
will infer the type
val to_binary_writer: (_ :> Stream) -> BinaryWriter
That is, because the constructor for System.IO.BinaryWriter accepts any subtype of System.IO.Stream, F# has also inferred that the derived function should accept any subtype of System.IO.Stream as its argument. You could also write this explicitly using:
let to_binary_writer (s :> Stream) = new BinaryWriter(s)
Here the pattern (s :> Stream) means 's should match a value whose type can be coerced to Stream'.
Type Inference and Checking
Type inference and checking of constraints is fairly straight-forward: each use of a value or type whose specification involves constrained type parameters will induce a constraint on the actual type parameters associated with the use of that item. (Aside: each time an ML value is used a fresh set of actual type parameters is generated - for example each time you write List.length a fresh inference type parameter is implicitly used as the actual type parameter for the value.)
Constraints are solved, or partially solved, as they are generated. For example:
The following limitations currently apply:
Extensions to the Grammar for Constrained Type Parameters
The extensions to the grammar are as follows:
<val-type> := | <type> -- unconstrained type | <type> when <constraints> -- constrained type
The following syntactic forms are for convenience. They imply constraints at the binding site related to the type variable (see below).
<type> := | <typar> :> <type> -- the same as <typar>, with an implied constraint | <typ> when <constraints> -- constrained type
The constraints can be of the following forms:
constraint := | <typar> :> <typ> -- the type parameter converts to the type | $<typar>.<method-name> : <method-type> -- overload constraint
Aside: Binding sites for type variables are inferred in the usual way for ML languages of the OCaml family. This binding site is either a value declaration, a type declaration. Inference variables will be bound at the value declaration where they are generalized, or if not generalized will have the scope of the entire file.
Extensions to the Semantics of Coercion Operators
The operator expr :> typ now means 'expr can be coerced to typ'. This includes the use of representation-changing conversions such as boxing.
Uses of Constrained Polymorphism in the F# Library
Pervasive Operators:
Overloading is supported for the following operators. The operators all default to operating over the int type should there be no other type information in the file to further constrain the use of the operator.
val (+): $a -> $b -> $a when $a.op_Addition : ($a, $b) -> $a val (-): $a -> $b -> $a when $a.op_Subtraction : ($a, $b) -> $a val ( * ): $a -> $b -> $a when $a.op_Multiply : ($a, $b) -> $a val (/): $a -> $b -> $a when $a.op_Division : ($a, $b) -> $a val (mod): $a -> $b -> $a when $a.op_Modulus : ($a, $b) -> $a val (~-): $a -> $a when $a.op_UnaryNegation : ($a) -> $a val (~+): $a -> $a when $a.op_UnaryPlus : ($a) -> $a val (land): $a -> $a -> $a when $a.op_BitwiseAnd : ($a,$a) -> $a val (lor): $a -> $a -> $a when $a.op_BitwiseOr : ($a,$a) -> $a val (lxor): $a -> $a -> $a when $a.op_ExclusiveOr : ($a,$a) -> $a val lnot: $a -> $a when $a.op_LogicalNot : ($a) -> $a val (lsl): $a -> int -> $a when $a.op_LeftShift : ($a,int) -> $a val (lsr): $a -> int -> $a when $a.op_RightShift : ($a,int) -> $a val (asr): $a -> int -> $a when $a.op_RightShift : ($a,int) -> $a
List, Stream, Vector, Array, Set:
The following functions are for transforming F# collections to and from .NET collections and now accept coerced arguments at F# call-sites:
List.of_IEnumerable: (_ :> IEnumerable<'a>) -> 'a list List.of_ICollection: (_ :> ICollection<'a>) -> 'a list Array.of_IEnumerable: (_ :> IEnumerable<'a>) -> 'a[] Array.of_ICollection: (_ :> ICollection<'a>) -> 'a[] Vector.of_IEnumerable: (_ :> IEnumerable<'a>) -> Vector<'a> Vector.of_ICollection: (_ :> ICollection<'a>) -> Vector<'a> Stream.of_IEnumerable: (_ :> IEnumerable<'a>) -> Stream<'a> Stream.of_ICollection: (_ :> ICollection<'a>) -> Stream<'a>
Idioms:
The following functions now accept coerced arguments at F# call-sites:
Idioms.foreach: (_ :> IEnumerable) -> ('a -> unit) -> unit Idioms.foldeach: (_ :> IEnumerable) -> 'acc -> ('acc -> 'a -> 'acc) -> 'acc Idioms.foreachG: (_ :> IEnumerable<'a>) -> ('a -> unit) -> unit Idioms.foldeachG: (_ :> IEnumerable<'a>) -> 'acc -> ('acc -> 'a -> 'acc) -> 'acc Idioms.using: (_ :> System.IDisposable) -> (unit -> 'a) -> 'a Idioms.lock: (_ :> System.Object) -> (unit -> 'a) -> 'a
The following functions are now checked more strictly.
Idioms.EnumToInt: 'a -> int when 'a :> System.Enum Idioms.IntToEnum: int -> 'a when 'a :> System.Enum Idioms.CombineEnumFlags: 'a list -> 'a when 'a :> System.Enum Idioms.TestEnumFlag: 'a -> 'a -> bool when 'a :> System.Enum
Fixed the following bugs:
282 fsyacc doesn't like // comments 267 interface data being attached to assemblies includes is bigger than it should be 286 NGEN of bootstrap compiler fails due to multiple mscorlib references. 291 bug in the implementation of stable_sort 292 Compiler bug - cannot find fslib library automatically 295 --standalone doesn't fold in debug symbols of the assemblies that have been read 293 alternative-install.bat gives a spurious error while loking for NGEN 296 multiple mscorlib references are appearing in --standalone assemblies (fscbng.exe) 302 VS plugin is not reloading referenced DLLs as they are recompiled (this forced users to restart VS to see changes) 303 Sys.time is returning Ticks not TotalSeconds 305 Implementing IEnumeratoron .NET v2.0 beta 2 is difficult due to inclusion of parent interfaces with identical method names
Fixed a bug related to inner polymorphic closures reported by Nikolaj Bjoerner (thanks Nikolaj!)
Fixed a bug related to over-applied polymorphic functions reported by Dominic Cooney (thanks Dominic!)
Implemented intellisense for further long-name lookups in VS integration, e.g. val.field.field or val.property.field
Fixed the following bugs:
272 Fixed: AV in VS Plugin when endlessly loading & unloading large F# projects 274 Fixed: A failure "stelem" occurred when compiling a test for .NET 2003 247 Fixed: test and document hashq operator 232 Fixed: verify non-generic assemblies using v1.0 and v1.1 peverify's 223 Fixed: Change test procedure to generate config files in order to test on various versions of the clr 275 Give better error messages when passing a value to a Params C# method 276 Give better error message when a CompatArray is needed
Added another DirectX Tutorial (Tutorial 1)
Fixed installer problems on VS 2003 - Babel package was still being registered with VS 2005
Reduce size of optimization information attached to F# DLLs.
Minor fixes to the Visual Studio installer.
Minor performance improvements for the command line compiler.
Fixed this bug:
268 F# Compiler: not all type definitions were being checked for cyclic abbreviations
Fixed one bug with intellisense where extra text on the line after the cursor was interfering with intellisense. Some glitches remain in this area.
Reduced the number of match-bracket calls to improve reponsivity of VS. Some bugs remain in this area.
Minor fixes to the Visual Studio installer.
Performance improvements for the command line compiler.
Fixed minor bugs with the recent additions to MLLib.
New reserved keywords after review of keyword policy: atomic, checked, class, decimal, event, pure. In addition the existing reserved word interface is now actually used as a keyword, and will hence give errors if used in your program. The others will give warnings.
Intellisense is now supported in the Visual Studio integration. Essentially all features are complete, though incorrect or slightly misleading information is occasionally be shown, and the correct context is not always available to allow information to be shown. You can turn off Intellisense globally by setting the environment variable FSharp_NoIntellisense=1.
Extended Object Expressions are now supported. This means objects created via object expressions can support multiple interfaces, which also makes F# a CLS Extender language according to the official definition of such things. The syntax is:
{ newwith interface with ... interface with }
e.g.
{ new Object() with Finalize() = cleanupFunction(false); interface IDisposable with Dispose() = cleanupFunction(true); }
Nested modules within a top-level module are now supported, e.g.
type ident = Id of string module Ident = struct let OfString(s) = Id(s) let ToString(Id(s)) = s end
and in the interface file
type ident module Ident : sig val OfString: string -> ident val ToString: ident -> string end
The atomicity of dynamic initialization is on the granularity of top-level modules, i.e all the bindings in a top-level module are executed for side-effects if any values in the file are required.
Patterns can now refer to .NET literals.
More .NET related functionality in the ML compatibility library. Modules Float, Float32, UInt32, UInt64, Stream. A much more systematic treatment of conversions between various integer and floating point types. Conversion functions to allow MLLib collections to be used as .NET collections (ICollection etc.). More efficient implementations of some functions.
Visual Studio for .NET will now work with Visual Studio 2003. See the installation instructions elsewhere in this file.
Controlling F# for Visual Studio if it starts to misbehave. The following global environment variables can be used to selectively control some of the features of F# for Visual Studio. They can also be set within the command shell where you execute devenv.exe if you run it explicitly.
set FSharp_Logging=1 set FSharp_LoggingVerbose=1 set FSharp_NoParsing=1 set FSharp_NoChecking=1 set FSharp_NoPriorInputParsing=1 set FSharp_NoConfigBuilding=1 set FSharp_NoPriorInputTypeChecking=1 set FSharp_NoTypeChecking=1 set FSharp_NoLexing=1 set FSharp_NoIntellisense=1
Documentation:
-- Revised grammar documentation in manual -- Revised interop decumentation in manual
Various bug fixes:
The following bugs were recorded as fixed in the F# bug database:
_ F# Compiler Implement setting of fields and properties on .NET value types for the simpe cases of mutable locals and byref arguments 196 F# Compiler Poor error message for signature mismatch when an abbreviation is hidden 211 F# Compiler too many long paths printed when using -i 204 F# Compiler fsyacc: newline missing 200 F# Compiler Newline or similar needed between errors 197 F# Compiler Can access hidden constructors and fields using long path names 137 F# Compiler Implement accessing generic methods 210 F# Compiler --standalone bug for winforms reported on f# list 259 F# Compiler Fixed a bug with generalizing variables: not all generalized type variables were being marked as rigid 255 F# Compiler The representation of discriminated unions that uses static fields and unique objects for unary constructors does not work for deserialized data 263 F# Compiler X-module optimization bug related to incorrectly fixed-up optimization data 183 F# Compiler Error messages from failed overloading can be poor (e.g. Text3D sample) 187 F# Compiler Error message when attempting to access a protected method from outside a subclass needs work 218 F# Compiler Add error when module name declaration is missing from intf or impl when filenames match e.g a.ml & a.mli 227 F# Compiler Signature checking of modules should be able to unify inference type variables 242 F# Language support #else in #if/#endif 194 F# Library Ensure float32, float etc. and other conversions are all complete and orthogonal in MLLib 249 F# Library Sys.time not correctly implemented 256 F# Library Added functions to Set, List and Stream to relate MLLib collections to .NET collections 206 F# Documentation export interop documentation needs work 212 F# Documentation Add 'differentiate' and other samples to the sdk 260 F# Documentation Update docs in parsing sample to reflect the presence of fsyacc and fslex 231 F# Visual Studio Plugin 'UncontrolledError' appears in error box when using VS 261 F# Release Visual studio mode fails to install if user has never started up visual studio since installing it
Various bug fixes: 33 26/11/2004 Abstract IL Generic constraints not correctly read/emitted in binary reader/writer 157 26/11/2004 F# VS VS should take into account the include path 180 02/12/2004 F# Compiler Private constructors may be visible to importing modules 185 10/12/2004 F# VS Problem when loading a F# project where a file did not exist 193 10/12/2004 F# Compiler Abstract IL and F# bug: errors when accessing fiels and methods where types have custom attributes 192 10/12/2004 F# Compiler upcasting from .NET array types to object reported a bogus warning about boxing, 191 10/12/2004 F# Compiler Errors are not reported at right location when argument types are wrong 186 10/12/2004 F# Language Cannot access protected methods on .NET objects 178 19/11/2004 F# Compiler Declaring a field of a given name blocks out any ability to access members of that name regardless of type annotations 177 19/11/2004 F# Compiler Some value recursive declarations incorrectly being labelled as "recursive data"
1. New compiler switches: --all-warnings: Print all warnings. --no-warnings: Do not print any warnings. --warn <int>: Report the given specific warning. --no-warn <int>: Do not report the given specific warning. --all-warnings-as-errors: Treat all warnings as errors. --warn-as-error <int>: Treat the given specific warning as an error. Warning numbers are printed as part of error messages and the less obvious ones will have further documentation in the manual including links to tutorials. 2. Better and fewer error messages for uses of value recursion. 3. Fixed a number of bugs: - Pretty much all uses of data constructors within value recursion declarations were incorrectly being labelled as "direct recursion" instead of "value recursion". - Field lookup was preferring ML-style field lookup over adhoc-name field lookup based on inferred type. This meant that declaring an F# field such as "Text" anywhere in your program meant that no adhoc lookup on "Text" would ever be resolved. - Type information was not being propagated correctly from outside-in for "let rec" bindings.
1. Change the compilation model to compile assemblies in one shot like C#. This gets rid of ALL the .cno, .cni, .cnx and .cnw files, leaving just a DLL. The compiler will add a custom attribute to store the F# interface and optimization data. For this version this will be brittle under changes to the F# compiler, but we will revisit that in later versions to arrive at a stable metadata format. 2. Cleanup code in preparation for the long-awaited source release. In particular * The parser had several of uppercase/lowercase distinctions left over from my initial version of a parser for the Caml syntax. These don't apply to F# which disambiguates more identifiers at a later stage. * Some optimizations have been rearranged, so less is done in the backend phase of the compiler. This greatly simplifies the code. * The treatment of the decision trees that arise from pattern matching was too complex. This has been simplified. 3. Add more support for making F# code highly usable from C# and other .NET languages. I have the long term aim to make sure that simply no one can tell something is written in F# if you don't want them to. The design for this is somewhat up in the air, but will almost certainly involve attributing the F# code to indicate how it should look from C# and other .NET languages. 4. Cleanup namespaces. All F# stuff is now in Microsoft.FSharp. All built-in types like list, option, ref, etc. will also be defined there. From C# they will be called List, Option, Ref etc. Microsoft.FSharp.MLLib.Pervasives Microsoft.FSharp.MLLib.String Microsoft.FSharp.MLLib.List etc. This has obvious advantages, and allows for an F#-specific library in the future, and perhaps even other libraries and source syntaxes to provide some level of mix-n-match for other functional programming languages. 5. Generate generic code by default. Non-generic code for use with versions of the CLR prior to Whidbey will need a command line option, e.g. "-no-generics" 6. Revisit subtyping, especially w.r.t. overloading, upcast, downcast etc. Casting and nulltest operations for .NET types are now built-in as primitive syntactic forms in F#. expr == | e :? ty -- dynamically test if 'e' has type 'ty'. A compile-time error will occur if local type inference does not infer types such that this is a valid downward type test. | e :> ty -- statically upcast 'e' to type 'ty'. A compile-time error will occur if local type inference does not infer types such that this is a valid upcast. | e :?> ty -- dynamically downcast 'e' to type 'ty'. A compile-time error will occur if local type inference does not infer types such that this is a valid downcast. | downcast e -- runtime checked downcast from 'e' to an arbitrary type inferred from the context. A compile-time error will occur if local type inference does not infer types such that this is a valid downcast. | upcast e -- statically checked upcast from 'e' to an arbitrary type inferred from the context. A compile-time error will occur if local type inference does not infer types such that this is a valid upcast. | e1 ?? e2 -- dynamically test if 'e1' is null, and if so evaluate e2. A compile-time error will occur if local type inference does not infer types such that e1 is a .NET type. Equivalent to (match e1 with null -> e2 | freshv -> freshv) | null -- generate a null value of an arbitrary type inferred from the surrounding context. A compile-time error will occur if local type inference does not guarantee that the type of the value is definitely a .NET reference type. pat == | null -- the pattern corresponds to a null test. A compile-time error will occur if local type inference does not ensure that the value being matched against is a .NET reference type. | :? ty -- the pattern corresponds to a .NET type test. A compile-time error will occur if local type inference does not infer types such that this is a valid downward type test. | :? ty as id -- the pattern corresponds to a .NET type test, and if successful the variable 'id' is bound to the value at the given type. Examples: a. Doing a null test in a pattern match: let d = new OpenFileDialog() in match d.OpenFile() with | null -> Printf.printf "Ooops... Could not read the file...\n" | stream -> ... let r = new StreamReader(stream) in Printf.printf "The first line of the file is: %s!\n" (r.ReadLine()); b. Doing a null test in an expression: let myReader = new StreamReader(new FileStream("hello.txt")) in while true do Console.WriteLine(myStream.ReadLine() ?? raise End_of_file); done; Valid casts are those between .NET types related by class extension or interface inheritance, and also between F# reference types and the type 'obj' (i.e. System.Object). Thus F# values can be stored in heterogeneous collections such as System.Collections.ArrayList. Local type inference is underspecified in this version of F#. In a future version of F# this will be adjusted to correspond to the process of using only "definite" type information in order to make a compile-time assessment, i.e. type information from external libraries and modules, from user type annotations, from uses of F# discriminated unions and F# record labels, and from other similar type information that arises directly from F# syntactic forms. In this version of F# local type inference applies applies all type information available to the left of a term, including the use of type equivalences inferred via Hindley-Milner style unification. (TODO) 7. Allow attributes to be declared. Syntax proposed is "[]" in various places. This is not yet settled and is certainly up for discussion. 8. Typesafe OCaml-style 'printf' is now supported. See the Printf library module. 9. Object expressions are now supported. 9a. Basic object expressions. An object expression declares an implementation and/or extenstion of a class or interface. For example, "{new IComparer with Compare(a,b) = if a < b then -1 else if b < a then 1 else 0 }" In each example below the "{new ... with ... }" expression generates a new class underneath the hood that implements/extends the given type. Note how little type information you need: the required signatures for OnPaint, Compare, ToString etc. are inferred by looking at the unique virtual method that we must be overriding. Note too how the anonymous classes close over free variables (e.g. capture the variable "n") behind the scenes. open System open System.Collections open System.Windows.Forms let myComparer = {new IComparer with Compare(a,b) = compare a b} let myFormattable = {new IFormattable with ToString(fmt,fmtProvider) = " "} let myForm title n = let res = {new Form() with OnPaint(paintEventArgs) = Printf.printf "OnPaint: n = %d\n" n and OnResize(eventArgs) = Printf.printf "OnResize: n = %d\n" } in res.Text <- title; res You may only override/implement methods in anonymous classes. You may not declare fields or new methods. To override/iumplement virtual properties you should override the "get_PropertyName" and "set_PropertyName" methods that represent the property under-the-hood. Likewise to override virtual events you should override the "add_EventName", "remove_EventName" and "fire_EventName" methods that represent the event under-the-hood. 9b. Accessing "this" in object expressions. There is no "this" keyword, and for good reasons: 1. In object-oriented languages an object may not be sucessfully constructed at many points where "this" can be used. These leads to inherent weaknesses in the initialization-soundness guarantees of these languages. 2. The natural typing for "this"/"self" would reveal the anonymous type of the implementation. Object expressions are for defining implementations, not types, and it is not desireable to mix type-generation with object expressions unnecessarily. This is similar to the way the discriminants of discriminated unions are not types in F# (though they may be types in the underlying IL). 3. Sometimes you want to access not "this"/"self" but another object in a self-referential graph of objects. There is nothing fundamentally different between doing that and accessing "this" or "self". However, you can access "this" by using the "reactive recursion" feature described elsewhere in these notes. This feature results in a compiler warning that the initialization guaranteed for the object expression may not be as strong as you wish, and in particular that (in theory) the constructor for the object may invoke a virtual method which uses "this" before the object is initialized. For example let rec obj = {new System.Object() with GetHashCode() = (obj.ToString()).Length} Here the identifier "obj" plays the role of "self" or "this". This example makes it plainer why "let rec" must be used: obj is certainly defined in terms of itself, i.e. its definition is self-referential or recursive. Note that mutually-self-referential objects can be defined via the same mechanism: let rec obj1 = {new System.Object() with GetHashCode() = (obj2.ToString()).Length} and obj2 = {new System.Object() with GetHashCode() = (obj1.ToString()).Length} Thus the primitive is self-referentiality via "reactive recursion" rather than allowing all object expressions to access "this". 9c. How to access the base class members, e.g. C#'s base.OnPaint() An inescapable part of the design of .NET object-oriented libraries is that they require extensions of some class types to use the implementations of overriden methods as part of the definition of the extension. This is seen in C#'s "base" identifier. F#'s permits object expressions that extend classes to be defined partly in terms of the base functionality of that class. This is done be labelling that functionality as part of the object expression: {new Form() as base with ... } Here "base" is not a keyword, just as "this" and/or "self" are not keywords. Any identifier can be used for base, and if object expressions are nested then different identifiers should be used at different points. In the example, the variable "base" has type "Form", and can only be used to perform method invocations, e.g. as follows: let myForm = {new Form() as base with OnPaint(args) = base.OnPaint(args); Printf.printf "OnPaint\n" n and OnResize(args) = base.OnResize(args); Printf.printf "OnResize\n" n } 9d. Implementing multiple interfaces This is not supported in this release. 10. Allow F# exceptions to be mapped to/from .NET exceptions, especially in cases like Out_of_memory and Stack_overflow. 11. Further optimizations: - Standard functional language optimizations for expressions known to be strings, constructors and tuples - Lift additional closed expressions to the top level - Optimize away inner polymorphism when functions are only used at one type 13. Extensive testing for all optimizations (a number of bugs were fixed which meant optimizations weren't always firing.) 14. Fix a bunch of minor bugs * Too much stuff was being made public. This was hurting abstraction and interop. * Precedence bug with infix operators such as "&&&&" and "||" * Improved various error messages * Minor stuff with calling methods on structs * A bug with Flush on StreamWriters reported by Dominic Cooney (thanks Dominic!) * Some bugs with error reporting reported by Alain Frisch (thanks Alain!) * A bug meant that we weren't turning tailcalls into loops for recursive public functions * A bug meant that an obscure internal error such as "undefined type variable: 'a567" was sometimes being reported. Appears to be the same as a bug reported by Dominic Cooney (thanks Dominic!) * Robert Pickering reported various bugs, including one involving delegates (thanks Robert!) * Creation of delegates taking no arguments (e.g. ThreadStart) had a bug * Fixed a typechecker bug with indexer properties * A couple of error messages weren't being printed, resulting in messages such as 'Tc.Fields_from_different_types(_, _, _)' * Fixed a couple of bugs with parsing floating point numbers * int_of_float now truncates the floating point number toward zero, rather than rounding. This is in line with the OCaml specification. While it's not clear that this is the desired behaviour, it seems appropriate that the items in Pervasives should follow the semantics of OCaml as closely as possible. * Fixed many bugs associated with F# values accessed using long paths, e.g. Microsoft.FSharp.Some("3") is a valid way to refer to Some("3"). * The number of compiler generated names appearing in the output has been reduced, and the internally generated identifiers that are produced have been made simpler, with fewer numbers appearing. * Can now reference multi-module assemblies. * Fixed a bug with the shift operators reported by Dominic Cooney (thanks Dominic!) * Fixed a bug with the ConcurrentLife sample's repainting behaviour reported by Dominic Cooney (thanks Dominic!) 15. Support 64-bit, 16-bit, 8-bit constants, and unsigned versions of the same, 62y: 8-bit signed byte 62uy: 8-bit unsigned byte 62s: 16-bit signed 62us: 16-bit unsigned 62l: 32-bit signed 62ul: 32-bit unsigned 62L: 64-bit signed 62UL: 64-bit unsigned The use of 'l' is a little unfortunate for 32-bit integers, since in C/C++-land it means "long", i.e. 64-bit. However the above is consistent with OCaml's use of constants, and there is no point being needlessly inconsistent. Literals of type 'bytearray' are now supported, with syntax B"abc\n" etc. Literals with syntax "abc\n" are unicode strings as previously. Literals of type 'byte' are now supported, with syntax B'\n', B'0', B'\t', B'a', B'\212' etc. Literals with syntax '\n', '0', '\t', 'a' '\212' etc. are unicode characters as previously. 16. Greatly improve debug support by getting more accurate sequence points. 17. Unicode escape sequences in identifiers and strings, ala C# style, i.e. \uXXXX and \UXXXXXXXX Somewhat crude support for authoring files in UTF-8 encoding is also supported. Unicode UTF-8 input may be used, where unicode characters are only currently allowed to appear in strings. Unicode characters in identifiers will also currently parse, but no true support is offered here: in particular error messages will be garbled, and we do not restrict identifier characters to the expected set of alpha-numeric equivalent characters. 18. Support for commercially use by permitting deployment of the F# library if it is statically linked into an application. The --standalone switch statically links the F# library and all referenced DLLs that depend on that library into the assembly (normally a .EXE) being produced. A verbatim copy is made of all the types and other definitions in all these DLLs and the copy is added to the assembly produced. Although these types may have the same name as the original types in the C# library, they are logically distinct. 19. Operators. The following operators have always been supported by F#, but are now user definable, rather than being tied to arrays and unicode strings. .[] .() .[]:= .():= This can help with porting OCaml code to F#, since the operations .[] and .[]:= can now be redefined to act on bytearrays rather than unicode strings. e.g. let (.[]) s n = Bytearray.get s n let (.[]<-) s n m = Bytearray.set s n m let (.()) s n = Microsoft.FSharp.Compatibility.CompatArray.get s n let (.()<-) s n = Microsoft.FSharp.Compatibility.CompatArray.set s n The operators ~- and ~-. have always been available in OCaml but not previously in F#. These let you redefine the unary "-" and "-." operators respectively, e.g. let (~-) n = Int64.neg n 20. Accessing .NET: Indexer properties. Indexer properties can now be accessed with the following natural syntax: let s = "abcdef" in s.Chars(2) // returns 'c' and for properties with C# syntax 'x[n]' use: x.Item(n) e.g open System.Collections; let f (x:ArrayList) = x.Item(0) 21. F#'s version of "recursion through data types using 'let rec'" to create "infinite" (i.e. self-referential) data structures is now slightly more restricted. You can't use recursive 'let rec' bindings through immutable fields except in the assembly where the type is declared. This means let rec x = 1 :: x is not supported. This was required to make sure the head/tail fields of lists are marked "private" and "initonly" in the underlying assembly, which is ultimately more important than supporting all variations on this rarely-used feature. However note that type node = { x: int; y: node} let rec myInfiniteNode = {x=1;y=myInfiniteNode} is still supported since the "let rec" occurs in the same assembly as the type definition, and type node = node ref let rec myInfiniteNode = { contents = myInfiniteNode } is supported since "contents" is a mutable field of the type "ref". 22. Nice-Compiled-Forms: Access of discriminated unions from other .NET languges has been improved. F# data types are compiled to C# classes with additional support to access the information carried by the data type. Constructing values of F# discriminated unions ---------------------------------------------- A static method is supported for each discriminant, e.g. List<int>.Cons(3,null) Option<int>.Some(3) Discriminating on F# discriminated unions ----------------------------------------- The following section applies if the type has more than one discriminant. The support depends very marginally on when "null" is used by the F# compiler as a possible representation of value of the data type. This is because types where "null" is used as representation cannot support some instance members (they would result in a null pointer exception when used from C#). "null" will ONLY be used by the F# compiler in EXACTLY the following situations: - For the single value of the "unit" type - For discriminated unions with exactly 2 constructors, where exactly one of those constructors is nullary - of course 'null' is then used for as the representation for the nullary constructor. Thus "null" may be used for the "list" and "option" types, and types very similar to these, but will rarely be used for other datatypes. If "null" is NOT used as representation then a type will support (a) a Tag property and several tag_... compile-time constants. These can be used for discrimination by switching. (b) a series of IsXYZ() instance methods, one for each constructor. These can be used for discrimination by predicates. If "null" IS used as representation then a type will support (a) a GetTag() static method, and several tag_... compile-time constants. These can be used for switching In the latter case discrimination by predicates can be performed by comparing values to "null", since the null value is then guaranteed to be used for the single nullary constructor of the type. Thus if the C# value x has type MyType and the F# definition of MyType is: type MyType = A of ... | B of ... | C of ... then the following C# code is valid: Console.WriteLine("{0}", x.IsA()); Console.WriteLine("{0}", x.IsB()); switch (x.Tag) { case MyType.tag_A: Console.WriteLine("A"); break; case MyType.tag_B: Console.WriteLine("B"); break; default: Console.WriteLine("Must be a C"); break; } 23. Nice-Compiled-Forms: Names for tuple types have changed The compiled form of tuple type names and tuple member names has been improved, e.g. Tuple<_,_>.Item1 Tuple<_,_>.Item2 Tuple<_._,_>.Item1 Tuple<_._,_>.Item2 Tuple<_._,_>.Item3 Tuple<_._,_,_>.Item1 Tuple<_._,_,_>.Item2 Tuple<_._,_,_>.Item3 Tuple<_._,_,_>.Item4 ... Tuple<_._,_,_,_,_,_>.Item1 ... Tuple<_._,_,_,_,_,_>.Item7 The compiled forms for tuples of size > 7 are under-specified. The above names change slightly when targetting .NET 1.0 or 1.1, i.e. a non-Whidbey release, because we then can't use the arity of the generic type to disambiguate the various "Tuple" names. So the names become Tuple2, Tuple3 etc. to Tuple7. 24. Nice-Compiled-Forms: Data fields compiled as properties, not fields Properties are now used to compile all memebers of F# record types, i.e. type recd = { Name: string; Size: int } will support recd.Name (a property, not a field) recd.Size (a property, not a field) 25. Nice-Compiled-Forms: Fields of alternatives can have names. F# discriminated unions can now have named fields, e.g. type 'a list = Nil | Cons of { Head: 'a; Tail: 'a list } Currently this information is ONLY present for describing the .NET view of such a type. The use of such a name leads to the creation of a .NET property with the given name. Thus there are strict limitations: - The names may not currently be repeated amongst different alternatives. - These fields may not be selected from F# code - Pattern matching against this form is not yet supported. - That is, for all other purposes the declaration is treated as if the fields were declared sequentially as normal, i.e. | Cons of 'a * 'a list These restictions may be lifted in a future release. The inbuilt list and option types are treated as if they have this form, i.e. type 'a list = Nil | Cons of { Head: 'a; Tail: 'a list } type 'a option = None | Some of { Item: 'a } and thus C# code can use List<int> x = List<int>.Cons(3,(List<int>.Nil)); Console.WriteLine("x.Head = {0}", x.Head); Console.WriteLine("x.Tail = {0}", x.Tail); Console.WriteLine("x.Tail - IsNil = {0}", x.Tail); 26. (This section only applies when targeting a CLR that supports generics, i.e. when the --no-generics switch is NOT used. ) The compiled form of function types has been finalized to be Microsoft.FSharp.FastFunc<A,B>. This will not change, though the details of the implementation of the Microsoft.FSharp.FastFunc class may be revised. FastFunc<A,B> is not a delegate type (which may be what some users expect). This option has been finally rejected for both interoperability and performance grounds. Creating function values that accept one argument ------------------------------------------------- It it important to be able to create and use values of this type from C# in a fashion that is as easy and natural as possible. One option is to create function values by using subclasses of Microsoft.FSharp.FastFunc<A,B> that override the "Invoke" method. However this is not the recommended way of creating such values, since it is then not so easy to use C#'s anonymous delegate feature when creating delegates. The most uniform way to create a FastFunc is to use an anonymous delegate. You simply create an appropriate .NET function-like delegate (e.g. System.Converter) and then call Microsoft.FSharp.FuncConvert.ToFastFunc. In particular, FuncConvert.ToFastFunc(...) supports the following overloads: ...(System.Converter<T,U> f) producing FastFunc<T,U> ...(System.Action<T> f) producing FastFunc<T,object> ...(System.Predicate f) producing FastFunc<T,bool> Additionally, there is an implicit conversion from System.Converter<T,U> to FastFunc<T,U>, and thus you can omit the call to FuncConvert.ToFastFunc() but ONLY when reating a delegate of type System.Converter<A,B> (for some A,B). For example, the following are equivalent: List.map (FuncConvert.ToFastFunc((Converter<int,string>) delegate(int x) { return x.ToString() + x.ToString(); }), myList); and List.map ((Converter<int,string>) delegate(int x) { return x.ToString() + x.ToString(); }, myList); Creating curried function values that accept multiple arguments --------------------------------------------------------------- The above techniques works well when creating F# function values that expect one argument. However the above can be awkward when creating F# function values that accept multiple values, whether by "currying" or by "tupling". Thus the F# library defines additional similar types in order to support additional conversions. In particular it defines delegate void System.Action<A1,A2>(A1 x, A2 y); delegate void System.Action<A1,A2,A3>(A1 x, A2 y,A3 z); delegate B System.Converter<A1,A2,A3,B>(A1 x, A2 y,A3 z); and Microsoft.FSharp.FuncConvert.ToFastFunc(...) method supports the following overloads that produce "curried" functions: ToFastFunc(System.Converter<A1,B> f) producing 'A1 -> B', i.e. FastFunc<A1,B> ToFastFunc(System.Converter<A1,A2,B> f) producing 'A1 -> A2 -> B', i.e. FastFunc<A1,FastFunc<A2,B>> ToFastFunc(System.Converter<A1,A2,A3,B> f) producing 'A1 -> A2 -> A3 -> B', i.e. FastFunc<A1,FastFunc<A2,FastFunc<A3,B>> > ToFastFunc(System.Action<A1> f) producing 'A1 -> unit', i.e. FastFunc<A1,object> ToFastFunc(System.Action<A1,A2> f) producing 'A1 -> A2 -> unit', i.e. FastFunc<A1,FastFunc<A2,object> > ToFastFunc(System.Action<A1,A2,A3> f) producing 'A1 -> A2 -> A3 -> unit', i.e. FastFunc<A1,FastFunc<A2,FastFunc<A3,object> > > For example: using System; using Microsoft.FSharp; using Microsoft.FSharp.MLLib; List<int> myList = List.of_array(new int[] { 4, 5, 6 }); string joined = List.fold_right<int,string> (FuncConvert.ToFastFunc((Converter<int,string,string>) delegate(int i,string acc) { return i.ToString() + "-" + acc; }), myList, ""); Creating function values that accept multiple arguments as tuples ----------------------------------------------------------------- To create F# function values that accept their arguments as tuples use Microsoft.FSharp.FuncConvert.ToTupledFunc. ToTupledFastFunc(System.Converter<A1,B> f) producing 'A1 -> B', i.e. FastFunc<A1,B> ToTupledFastFunc(System.Converter<A1,A2,B> f) producing 'A1 * A2 -> B', i.e. FastFunc<A1,FastFunc<A2,B>> ToTupledFastFunc(System.Converter<A1,A2,A3,B> f) producing 'A1 * A2 * A3 -> B', i.e. FastFunc<A1,FastFunc<A2,FastFunc<A3,B>> > ToTupledFastFunc(System.Action<A1> f) producing 'A1 -> unit', i.e. FastFunc<A1,object> ToTupledFastFunc(System.Action<A1,A2> f) producing 'A1 * A2 -> unit', i.e. FastFunc<A1,FastFunc<A2,object> > ToTupledFastFunc(System.Action<A1,A2,A3> f) producing 'A1 * A2 * A3 -> unit', i.e. FastFunc<A1,FastFunc<A2,FastFunc<A3,object> > > 27. Added List.of_array and List.to_array. 28. Initialization semantics for toplevel bindings have been changed to be more suitable for a multi-language, dynamic loading setting. - .EXEs: The only top-level bindings that are immediately evaluated on startup are those in the last module specified on the command line when building a .EXE. - .DLLs: All bindings in a DLL are executed on demand, at most once each time a module is loaded into a .NET application domain. The execution may be triggered by the access of any of the fields or methods of a module. The granularity is guaranteed to imply that all the top level bindings in a single F# source file are evaluated sequentially if any are evaluated. 29. The compiled type name for the type of hashtables is now Microsoft.FSharp.MLLib.Hashtbl.Hashtbl rather than Microsoft.FSharp.MLLib.Hashtbl.t This looks better in the debugger and from other .NET langauges. The latter (or equivalently Hashtbl.t) is still valid from F# code, and is a F# synonym for the former. In the future the compiled name of the type may be changed to be simply Microsoft.FSharp.MLLib.Hashtbl. The same applies to Lazy.t 30. The Buffer module has been implemented in MLLib and is a trivial wrapper for ML's Buffer.t type to .NET'd StringBuilder type. 31. Type operations The expression "e :? ty" is equivalent to a dynamic type test. A warning will be emitted if the type of e cannot be statically determined to be a subtype of ty (statically determined == using the inference information available at this point in the typechecking of the program, where inference proceeds left-to-right through the program). An error will be reported if the type test will always succeed. This is especially useful for testing for classes of .NET exceptions, e.g. try ... with | e when (e :? System.Net.Sockets.SocketException) -> ... | e when (e :? System.OutOfMemory) -> ... The expression "e as ty" is equivalent to an upcast. An error is given if the compiler cannot statically 32. Subsumption can now be used at method call sites where this disambiguates a method overload. That is, upcasts will now be allowed for arguments when calling .NET methods. The rule for resolving overloading will be: o we only consider overrides that match by name and arity (since we always know the number of arguments being applied) o if there is only one such method then each actual/formal argument must either match by an upcast or by unification o if there are multiple such methods then: - if the argument types of only one such method match by type equivalence then that one is used - otherwise if the argument types of only one such method match by type equivalence and/or upcasts then that one is used - otherwise report an error Here upcasts and type equivalence are performed in the sense of "local type inference" as described below. Thus the overload resolution is less aggressive than C# (no automatic boxing/implicit-conversions, less aggressive resolution of ambiguity between methods based on "best overload") but you can always specify the overload you want. 33. Runtime-checked reactive 'let rec' recursion is now supported. This means you can write values (and not just functions) whose specifications appear to refer to themselves, but where the self-references are hidden inside delayed values such as inner functions, other recursive functions, anonymous 'fun' lambdas, lazy computations, and the 'methods' of object-implementation expressions. A much broader class of expressions can now be used in 'let rec' bindings, and in particular the expressions can be applications, method calls, constructor invocations and other computations. The recursion is 'runtime checked' because there is a possibility that the computations involved in evaluating the bindings may actually take the delayed computations and execute them. The F# compiler gives a warning for 'let rec' bindings that may be ill-founded (i.e. bindings which it can't convince itself are well-founded), and inserts delays and thunks so that if runtime self-reference does occur then an exception will be raised. In the future the exception raised will be augmented to carry useful information such as line numbers and the dependencies in the bindings. The recursion is 'reactive' because it only really makes because it really only makes sense to use this when defining automaton such as forms, controls and services that respond to various inputs and make self-referential modifications as a result, e.g. GUI elements that store and retrieve the state of the GUI elements as part of their specification. A simple example is the following menu item, which prints out part of its state as part of its action: let rec menuItem : MenuItem = new MenuItem("&Say Hello", new EventHandler(fun sender e -> Printf.printf "Hello! Text = %s\n" menuItem.Text), Shortcut.CtrlH) A compiler warning is given when compiling this code because in theory the "new MenuItem" constructor could evalute the callback as part of the construction process, in which case a self-reference would have occured - and F# can't prove this won't happen. It is an current research topic in the language design community to design type systems to catch these cases, but in the context of .NET a relatively non-restrictive construct is needed since stateful components are unavoidable, and it would be difficult (if not impossible) to design libraries such as WinForms in such a way that a type system would prove the well-foundedness of the self-referential accesses. 34. Additional syntax forms supported: expr := ... | try <expr> finally <expr> -- always execute an exit block, even if an exception occurs topdecl := ... | module <long-identifier> -- specify the namespace/class for the generated bindings | do <expr> -- execute the given expression bindings := ... | do <expr> -- execute a statement as part of a recursive binding 35. New keywords For .ML, .MLI, .FS and .FSI files: finally, upcast, downcast, null, :> , :?> , :? Reserved for .FS and .FSI files: base constraint default namespace return switch enum extern fixed functor include inherit module object virtual sig using interface class volatile process method 36. Namespaces and module names can be specified with an initial declaration at the head of a file, e.g. module Microsoft.FSharp.MLLib.Pervasives module MyLibrary.MyModule The final identifier is treated as the module name, the former identifiers as the namespace. By default there is no namespace and the existing rule is used for the module name (i.e. the first letter of the filename is capitalized). A default namespace can be specified via the --namespace command line option. If any module ddeclaration is given it overrides both the default namespace and the rule for naming modules. 37. 'try-finally' blocks are supported 38. The Game of Life sample has been thoroughly revised to be a Multi-threaded GUI application showing how to control the progress of underlying computations as they execute. 39. C#/C++-style "//" comments-to-end-of-line are now supported. "(*" and "*)" have no special meaning inside this comment text. This is technically an incompatibility with OCaml code, however it is usually trivial to simply change the name of the "//" operator to something else. 40. An extra compiler switch --minimal-cli-version is supported by which you can specify the minimal .NET CLI required to run the generated binary. For example, even if you are developing using v2.0 (Whidbey) you can generate a bnary for use on V1.1 by using a combination of "--minimal-cli-version 1.1.4322" and "--no-generics", likewise "--minimal-cli-version 1.0.3705". The generated binary may, of course, still require library features from later versions. You should certainly run peverify from the appropriate version of the .NET SDK on the binaries to ensure they are correct. As an alterntaive to all of this you may also alternatively be able use a configuration file for fsc.exe to ensure the correct runtime is used when compiling - see examples such as ilasm.exe.config in your C:\WINDOWS\Microsoft.NET directory. 41. F# now supports C#-style #if/#endif conditional compilation. The command line switch is --define. The conditional compilation syntax to support mixed OCaml/F# programming has also changed a little (the old syntax was universally regarded as gross and was non-uniform). The syntax how is simply that text surrounded by (*IF-FSHARP ... ENDIF-FSHARP*) and (*F# ... F#*) are included when compiling with the F# compiler, and text surrounded by (*IF-CAML*) ... (*ENDIF-CAML*) (*IF-OCAML*) ... (*ENDIF-OCAML*) is excluded when compiling with the F# compiler. Of course the converse holds when compiling programs using the OCaml compiler. 42. F# now supports generation of retargetable .NET binaries. Retargetable binaries are neutral w.r.t. the publisher of particular referenced binaries. For example, a truly portable .NET program will be neutral w.r.t. the publisher of mscorlib - rather than picking up a dependency against the publisher of mscorlib used at compile-time. This means the program will bind to ANY assembly called "mscorlib", which is OK for mscorlib but is not necessarily such a good idea for other strong named assemblies. You can tell if you have generated retargetable references by using the ILDASM tool that comes with the CLR. If you have you will see: .assembly extern retargetable mscorlib { .publickeytoken = (96 9D B8 05 3D 33 22 AC ) // ....=3". .ver 2:0:3600:0 } .assembly extern retargetable System { .publickeytoken = (96 9D B8 05 3D 33 22 AC ) // ....=3". .ver 2:0:3600:0 } and so on. Public key tokens are used to help name a referenced assemblies. The particular public key token used above is the unique "neutral" key token as specified by the .NET ECMA spec. I have been told that Microsoft's Compact Framework (CF) counts as a second set of "published" assemblies, and so if you want to make binaries that can be executed on the CF and other versions of the CLR then you should use this option. The --retargetable option is used to indicate a particular assembly reference to make retargetable in the produced binary. There is no way to make all assembly references retargetable - you have to do it on a case-by-case basis. Furthermore the reference to "fslib" is not retargetable, since you have to use the fslib provided in the F# distribution. 43. F# now supports funcitons Map.Make and Set.Make that accept comparison functions and return record expressions that act as components. These are akin to OCaml's Set.Make and Map.Make functors. The release also includes Map, Set and Hashtbl implementations that use polymorphic comparison. Hashtbl.Make is yet to be done. 44. F# now supports "forall" types on fields. That is, fields can have types that logically speaking correspond to generic methods. This is useful when records are used to represent modules, e.g. with Map.Make and Set.Make's fold functions. 45. F# field names may now be used as part of the "." notation even when the field names have not been brought into the top level scope via an "open". For example if a module contains type recd = { Name: string } then you may use x.Name if x is determined by local type inference to be of type "r". This is subject to the same rules as .NET member lookups, i.e. the type of the value to the left of the "." must be determined by local type inference using annotations and other information available left-to-right, outside-to-inside. 46. Optimization improvements to "==" and polymorphic comparison. 47. A Visual Studio integration is now supported, with syntax highlighting, automatic parsing, automatic typechecking and a simple project system. 48. --target-winexe, --target-exe and --target-dll are now supported. 49. Pervasives now supports string-named versions of all the Caml-style operators defined in that file, e.g. op_GenericEquality for '='. See pervasives.fsi for the full list of names used. 50. A lexer generator along the lines of ocamllex/mosmllex is now supported. Input files identical to ocamllex files are supported, with the following exceptions: - using 'as' to label internal portions of matched strings is not supported - '#' regular expressions are not supported - 'shortest' matching is not supported - Command line options -o, -ml, -q are not supported The Lexing library module is supported with a specification similar to the specification of OCaml's Lexing module. This was coded from scratch. Most of the fields of the lexing state are hidden, adn thus differ from OCaml, with the exception of Lexing.lex_start_p and Lexing.lex_curr_p. 51. A parser generator along the lines of ocamlyacc/mosmlyacc is now supported. Input files identical to ocamlyacc files are supported. See the notes in the Parsing module in MLLib for some compatibility issues.