Using F# Interactive (FSI.EXE)

'F# Interactive' is the name of fsi.exe, the top-level command line interpreter for F#. The mode is still under development but ca ben used for many tasks and only runs on .NET 2.0 (Visual Studio 2005). Run it using bin\fsi.exe. Inputs to fsi.exe are terminated with ;; (nothing happens until you type this!)

Common switches are:

   --gui           run a session with a concurrent WindowsForms GUI event loop.
                   (Entered code is executed on the GUI thread, but the GUI thread
                   is not blocked waiting for input.)
   -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# Command Line - using the compiler

The compiler fsc.exe is a traditional command line compiler with a set of command line switches similar to both the OCaml compiler and the C# compiler. Detailed help can be found by running "fsc -- help". Some of the common command line flags are shown below, along with detailed instructions on the How to statically link the F# libary using the "--standalone" switch. Also see the notes on using F# in conjunction with alternative CLI implementations.

The most common command line arguments (FSC.EXE)
  • -o Name the output file.

  • -I Specify a directory for the search path for referenced assemblies.

  • -g Produce debugging information.

  • -O3 Turn on maximal optimization.

  • -a Produce a DLL.

  • -r Reference an F# or .NET DLL.

  • --keyfile Sign an assembly using the given keyfile. This must be done when creating DLLs that will be shared by multiple programs and installed in the "Global Assembly Cache" on target machines.  For example, the F# library itself is signed with a key.

The default is to build an executable. You can reference a C# or other .NET assembly simply by adding the DLL for the assembly to your command line with the "-r" option.

For example, to reference the assembly System.Windows.Forms.dll you provide System.Windows.Forms.dll on your F# command line:

fsc.exe -r System.Windows.Forms.dll -c mygui.ml

The DLLs for several assemblies in the .NET Redistributable are automatically added to every F# compilation. The .NET Framework installation directory for your machine is also searched when attempting to find named assemblies.

DLLs produced by F# can be used immediately from C# and other .NET languages without any further support files.  Simply use "/r:mydll.dll" on the C# or VB or other .NET language command line or add the assembly as a dependency in VisualStudio.NET.

Binaries produced with the "--debug" (-g) and "-O" (optimization) switches are binary compatible, i.e. different binaries in a target application can be compiled with different debugging and optimization settings. Binaries do also have some decent versioning properties.  For example, you may add new functions and types to the internal or externals of a module and the resulting DLL will be compatible with the original. You may also modify the interior definitions of a DLL, as long as you do not use cross-module optimization on clients of the DLL. If you use cross-module optimization then clients may have incorporated aspects of the definition of the values in the DLL into their own binary via inlining.

Statically linking the F# library using "--standalone"

The licence under which you use the F# compiler (see LICENCE.txt in the distribution) may include a clause indicating that binaries produced by the compiler can be redistributed under different (e.g. perhaps commercial-use) conditions if they use the --standalone compiler switch to statically link a copy of the F# library into the application. The rationale behind this is the wish to avoid the possiblity of users making mistakes in installing and deploying F# library components, so developers are requested to perform statically linking this. (The F# library module fslib.dll contains the definition of the basic types "list", "option", "ref" and primitives used to build other functional programming libraries. The F# library module mllib.dll contains the various types and modules defined in MLLib.

The --standalone switch will statically link the F# library and all transitively referenced DLLs that depend on the F# library into the assembly being generated (the "target assembly"). The assembly will typically be a .EXE representing a final application but may also be a DLL. Using this switch does the following things:

  • It does a graph traversal of all static assembly dependencies beginning with the non-standalone version of the target assembly to determine all the assemblies which depend on the F# library (which along with F# forms the "static linking set")

  • It takes a copy of the code and metadata of each assembly in the static linking set and places this into the target assembly.

  • It renames all types in the target assembly whose name begins with Microsoft.FSharp to a type beginning with FSharp. It also makes all such types private to the target assembly.

  • It adjusts all external assembly references from the static linking set so that they refer to the assemblies referenced during compilation.

  • It disables the generation of the F# interface and optimization attributes

There are some important restrictions on the use of this switch:

  • None of the assemblies in the static linking set may include native code or native resources.

  • None of the assemblies in the static linking set may use COM interop.

  • Some details of debug information may be lost for assemblies in the static linking set.

  • None of the transtively referenced DLLs may itself be a component containing a statically linked F# library.

  • There must be no duplicate type names across any statically linked assemblies

  • No code may use reflection on any of the types in those assemblies

  • No code may export F# library types as part of its public API.

  • The application must have been developed and tested using the "--standalone" switch.

  • The user of the compiler must re-run "peverify" over the contents of the generated binary to ensure the static linking process has produced a valid binary.

This means that the application and its dependent DLLs must only use F# library types "internally". It is typically simple to arrange this for an application that is an executable or a service, since these only have a small number of entrypoints. If the application is a DLL then the application can still be written entirely in F#, but the signatures of exported modules should include only exportable types. Exportable types are abstract, record, discrimination types, types from other .NET assemblies and type abbreviations whose target is itself exportable. Tuple types, first-class function values and types defined in the F# library (apart from those mapped directly to .NET types from other assemblies) are not exportable. Record and discrimination types are only exportable if all their constituent types are exportable types. Values with top-level function types of the form v: typ ->... typ -> typ may be exported, as long as each individual type is an exportable type.

Using F# in conjunction with other CLI implementations

In principle F# produces CLI programs that conform to the relevant ECMA standards. To produce programs that run on the SSCLI 1.0, Mono 1.0 or other .NET implementations you should certainly be sure to provide the "--cli-version 1.0" argument to the compiler.

  • Mono. Tailcalls have reported to cause problems on Mono, hence the switches --cli-version 1.0 --no-tailcalls are recommended. addition, the binary fscb10ntc.exe is a portable version of the F# compiler that is reported to work to some extent in conjunction with the Mono CLI implementation. The matching "ntc" versions of the libraries are also included. You will need to install these into the GAC using

           gacutil -i fslib10ntc.dll
           gacutil -i mllib10ntc.dll
    

    Note: Running this version of the compiler is reported to be sluggish on Mono, 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 program fsc.exe includes some components that connect to Windows native code, and is a Windows binary. It will run correctly on Windows XP (tm), Windows NT (tm), Windows 2000 (tm) and so on. This version of the compiler works best if used on a machine that has an installation of the Microsoft .NET Framework SDK and/or the Microsoft .NET Redistributable components. This is because the F# compiler detects the presence of these components to do several things:

  • Produce debug information files.

  • Support strong-name signing of assemblies.

  • Support strong-name signing of assemblies.

  • The installation scripts use the components to support the automatic installation of the F# library components.

If an installation of the appropriate components cannot be found then the F# compiler will not support the above operations, but should continue to produce usable binaries.

The F# compiler also makes an attempt to detect these components when an installation of the SSCLI is available. This is determined by looking for sscoree.dll on the path. If both the SSCLI and an installation of the Microsoft .NET reditributable components are on the same machine then the use of the SSCLI can be forced by using the "--sscli" switch on the compiler. This option is not regularly tested.