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 how to join the F# email list. You may be interested in tracking Don Syme's F# blog and/or blogging about your experiences with using F#.
Contents:
install-fsharp.bat
install-fsharp.bat installs the F# library and the ML compatibility library in the Global Assembly Cache on your machine so you can run the programs you compile from anywhere. It also schedules fsi.exe for .NET pre-compilation. Several flavours of these library may get installed, e.g. for use with early versions of the .NET Common Language Runtime (e.g. fslib10.dll - the '10' stands for 'version 1.0 or version 1.1'). F# will select the appropriate library based on the command line switches you use. You may install the libraries on other machines by copying appropriate files and running the install-fsharp.bat script there.
To install and use the 'F# for Microsoft Visual Studio' components, follow the following steps carefully.
1. Run install-fsharp.bat (see Compiler Installation).
2a. If you have Visual Studio 2005 (Beta 2 or later):
------- first run the interactive installer -------- .\FsVsPackageInstall.msi ------- after you've completed the above interactive installer run -------- "C:\Program Files\Microsoft Visual Studio 8\Common7\IDE\devenv.exe" /setup
2b. If you have Visual Studio 2003:
------- first run the interactive installer -------- .\FsVsPackageInstall.msi ------- after you've completed the above interactive installer run -------- "c:\Program Files\Microsoft Visual Studio .NET 2003\Common7\IDE\devenv.exe" /setup
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\PackagesAn installation in VS 2003 would change 8.0 to 7.1, and if using the VSIP SDK then add 'Exp'. (e.g. 7.1Exp and 8.0Exp). If the key doesn’t occur at all then installation of the F# Visual Studio components failed. If so, please report this problem to dsyme@microsoft.com.
The Visual Studio Debugger will work with F# code without any additional installation steps. Debugging information for the libraries may be found in the bin directory. In Visual Studio 2005 you may need to disable "Just My Code" debugging (use the "Options" menu). You can also set the appropriate symbol path there or you can add that path to the _NT_SYMBOL_PATH environment variable.
Let's write a program in a file main.fs, e.g.
let _ = print_endline "Hello world"
and the compile and run it using fsc.exe:
> c:\fsharp\bin\fsc main.fs > .\main Hello world
or enter it into fsi.exe:
C:\fsharp> bin\fsi MSR F# Interactive, (c) Microsoft Corporation, All Rights Reserved F# Version 1.1.3.2, compiling for .NET Framework Version v2.0.50215 > let _ = print_endline "Hello world";; Hello world > #q;;
Note the ";;" to terminate entries into fsi.exe.
Now let's write it another way:
do List.iter (fun s -> print_string s) ["Hello"; " "; "world"] do print_newline()
That shows you how to use function values (lambdas) and one function from the list library (nb. do is much the same as let _ = ). List processing is very common and powerful in functional programming. Of course we get the expected result when we run the above program.
Here's still another way, which shows you how to define and use new record and class types with properties and constructors, and also a call to Printf.printf :
type Data = { first: string; second: string; } type MyDataWrapper = class val data : Data new(data) = { data = data } member x.First = x.data.first member x.Second = x.data.second end let myData = new MyDataWrapper({ first="Hello"; second="world"; }) open Printf;; do printf "%s %s\n" myData.First myData.Second
(Note: of course you could do this all with just one class with two val fields, but then again you could do the whole program in one line :-))
And here's another way, which shows you that you can call the .NET standard libraries instead of the min-functional programming library provided with F#:
do SystemoConsole.WriteLine("Hello world")
Finally, to write components you will often want to write signature files (.fsi). Here's a very simple interface file:
test.fsi: val double: int -> int val four: int test.fs: let double x = x + x let four = double (double 1)
The command line compiler is bin\fsc.exe. The samples give a guide to using the compiler, both alone and in conjunction with other .NET programming tools. Some of the samples require the Microsoft .NET Framework SDK.
Common switches are:
-a build a DLL (an archive) -g generate debugging information -O3 maximal optimization -I add a path to search for compiled interface files
Files produced include:
abc.dll a compiled .NET assembly (use the -a flag) abc.exe a compiled .NET executable abc.pdb Windows debugging information abc.ildb SSCLI debugging information
'F# Interactive' is the name of the top-level command line interpreter for F#. The mode is still under development but ca be used for many tasks and only runs on .NET 2.0 (Visual Studio 2005). Run it using bin\fsi.exe. The TermWalker sample gives an example script you can paste line-by-line into the top-level environment. You can also compile this script file using fsc.exe. Inputs to fsi.exe are terminated with ;; (nothing happens until you type this!)
Common switches are:
--gui run a session with a GUI event loop.build a DLL (an archive) -O3 run a session that produces maximally optimized code on-the-fly -I add a path to search for compiled interface files -r reference a DLL
Common meta-commands you can enter into fsi.exe are:
#r;; dynamically reference the given DLL. #I ;; add the given include path. #use ;; load the given file. #time;; toggle timing on/off. #quit;; quit the session.
The first two of these can also be used directly in F# source code files. It can also be useful to quote filenames using 'verbatim strings', e.g.
#r @"c:\fsharp\bin\absil10.dll";;
Finally, F# Interactive currently uses non-generic code, and so any referenced F#-compiled assemblies must have been compiled using --no-generics. It is common practice to suffix such files with 10, as in the example above.
F# comes with three libraries.
The source code for fslib.dll and mllib.dll are included as sample code. MLLib gives a standard functional progrmaming library that lets you write code that will cross-compile as both OCaml and F# code. However, you are not committed to using MLLib, as all the primitives required for building your own functional programming library are provided in fslib.dll (use the source code to MLLib as a guide).
All compilations currently implicitly reference both mllib.dll and fslib.dll, though the former can be disabled using the --no-mllib command line option. However, you may also use F# as a 'pure' .NET programming language, without relying on constructs defined in mllib.dll, i.e. F# code need not rely on mllib.dll, since you can program directly against the .NET libraries.
The interface files that define F# Core Library and MLLib can be found in the lib/fslib and lib/mllib directories. The most important files are:
A subset of this library can also be used to help write code that can be compiled using either F# or OCaml. Cross-compilation may require some conditional compilation, for example to build abstractions that account for the differences between F# and OCaml, e.g. the fact that OCaml strings are not Unicode. See the manual for details on how to use conditional-compilation in F#.
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 obejct 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 CHsahSet.
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. The binary fscb10ntc 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.
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. 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.
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 install-fsharp.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: Report the given specific warning. --no-warn : Do not report the given specific warning. --all-warnings-as-errors: Treat all warnings as errors. --warn-as-error : 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 .Cons(3,null) Option .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 x = List .Cons(3,(List .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 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 f) producing FastFunc ...(System.Action f) producing FastFunc ...(System.Predicate f) producing FastFunc Additionally, there is an implicit conversion from System.Converter to FastFunc , and thus you can omit the call to FuncConvert.ToFastFunc() but ONLY when reating a delegate of type System.Converter (for some A,B). For example, the following are equivalent: List.map (FuncConvert.ToFastFunc((Converter ) delegate(int x) { return x.ToString() + x.ToString(); }), myList); and List.map ((Converter ) 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 x, A2 y); delegate void System.Action (A1 x, A2 y,A3 z); delegate B System.Converter (A1 x, A2 y,A3 z); and Microsoft.FSharp.FuncConvert.ToFastFunc(...) method supports the following overloads that produce "curried" functions: ToFastFunc(System.Converter f) producing 'A1 -> B', i.e. FastFunc ToFastFunc(System.Converter f) producing 'A1 -> A2 -> B', i.e. FastFunc > ToFastFunc(System.Converter f) producing 'A1 -> A2 -> A3 -> B', i.e. FastFunc > > ToFastFunc(System.Action f) producing 'A1 -> unit', i.e. FastFunc ToFastFunc(System.Action f) producing 'A1 -> A2 -> unit', i.e. FastFunc > ToFastFunc(System.Action f) producing 'A1 -> A2 -> A3 -> unit', i.e. FastFunc >> For example: using System; using Microsoft.FSharp; using Microsoft.FSharp.MLLib; List myList = List.of_array(new int[] { 4, 5, 6 }); string joined = List.fold_right (FuncConvert.ToFastFunc((Converter ) 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 f) producing 'A1 -> B', i.e. FastFunc ToTupledFastFunc(System.Converter f) producing 'A1 * A2 -> B', i.e. FastFunc > ToTupledFastFunc(System.Converter f) producing 'A1 * A2 * A3 -> B', i.e. FastFunc > > ToTupledFastFunc(System.Action f) producing 'A1 -> unit', i.e. FastFunc ToTupledFastFunc(System.Action f) producing 'A1 * A2 -> unit', i.e. FastFunc > ToTupledFastFunc(System.Action f) producing 'A1 * A2 * A3 -> unit', i.e. FastFunc >> 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 finally -- always execute an exit block, even if an exception occurs topdecl := ... | module -- specify the namespace/class for the generated bindings | do -- execute the given expression bindings := ... | do -- 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.