Import declarations: The open keyword

An import declaration specifies a module or namespace whose elements you can reference without using a fully qualified name.

Syntax

open module-or-namespace-name
open type type-name

Remarks

Referencing code by using the fully qualified namespace or module path every time can create code that is hard to write, read, and maintain. Instead, you can use the open keyword for frequently used modules and namespaces so that when you reference a member of that module or namespace, you can use the short form of the name instead of the fully qualified name. This keyword is similar to the using keyword in C#, using namespace in Visual C++, and Imports in Visual Basic.

The module or namespace provided must be in the same project or in a referenced project or assembly. If it is not, you can add a reference to the project, or use the -reference command-line option (or its abbreviation, -r). For more information, see Compiler Options.

The import declaration makes the names available in the code that follows the declaration, up to the end of the enclosing namespace, module, or file.

When you use multiple import declarations, they should appear on separate lines.

The following code shows the use of the open keyword to simplify code.

// Without the import declaration, you must include the full
// path to .NET Framework namespaces such as System.IO.
let writeToFile1 filename (text: string) =
  let stream1 = new System.IO.FileStream(filename, System.IO.FileMode.Create)
  let writer = new System.IO.StreamWriter(stream1)
  writer.WriteLine(text)

// Open a .NET Framework namespace.
open System.IO

// Now you do not have to include the full paths.
let writeToFile2 filename (text: string) =
  let stream1 = new FileStream(filename, FileMode.Create)
  let writer = new StreamWriter(stream1)
  writer.WriteLine(text)

writeToFile2 "file1.txt" "Testing..."

The F# compiler does not emit an error or warning when ambiguities occur when the same name occurs in more than one open module or namespace. When ambiguities occur, F# gives preference to the more recently opened module or namespace. For example, in the following code, empty means Seq.empty, even though empty is located in both the List and Seq modules.

open List
open Seq
printfn %"{empty}"

Therefore, be careful when you open modules or namespaces such as List or Seq that contain members that have identical names; instead, consider using the qualified names. You should avoid any situation in which the code is dependent upon the order of the import declarations.

Open type declarations

F# supports open on a type like so:

open type System.Math
PI

This will expose all accessible static fields and members on the type.

You can also open F#-defined record and discriminated union types to expose static members. In the case of discriminated unions, you can also expose the union cases. This can be helpful for accessing union cases in a type declared inside of a module that you may not want to open, like so:

module M =
    type DU = A | B | C

    let someOtherFunction x = x + 1

// Open only the type inside the module
open type M.DU

printfn "%A" A

Namespaces That Are Open by Default

Some namespaces are so frequently used in F# code that they are opened implicitly without the need of an explicit import declaration. The following table shows the namespaces that are open by default.

Namespace Description
FSharp.Core Contains basic F# type definitions for built-in types such as int and float.
FSharp.Core.Operators Contains basic arithmetic operations such as + and *.
FSharp.Collections Contains immutable collection classes such as List and Array.
FSharp.Control Contains types for control constructs such as lazy evaluation and async expressions.
FSharp.Text Contains functions for formatted IO, such as the printf function.

AutoOpen Attribute

You can apply the AutoOpen attribute to an assembly if you want to automatically open a namespace or module when the assembly is referenced. You can also apply the AutoOpen attribute to a module to automatically open that module when the parent module or namespace is opened. For more information, see AutoOpenAttribute.

RequireQualifiedAccess Attribute

Some modules, records, or union types may specify the RequireQualifiedAccess attribute. When you reference elements of those modules, records, or unions, you must use a qualified name regardless of whether you include an import declaration. If you use this attribute strategically on types that define commonly used names, you help avoid name collisions and thereby make code more resilient to changes in libraries. For more information, see RequireQualifiedAccessAttribute.

See also