Table of Contents

Namespaces, imports, and aliases

Namespaces organize types and functions and prevent names from colliding. Imports make those names shorter to use, while aliases provide a different local name when that improves clarity or resolves a conflict.

Use a file-scoped namespace when all declarations in a file belong together:

namespace Foo

// Members here

The namespace name may contain several dot-separated parts, such as Acme.Payments.Models.

Imports

An import makes the members of a namespace available without repeating the qualified namespace name:

namespace Foo

import System.*
import System.Collections.*

// Members here

Imports make existing names available; they do not create a new name. The wildcard may also be applied to a type name to bring its static members and nested types into scope:

import System.Math.*

let pi = PI

Enum members are constants and participate in type-member imports. A wildcard type import such as import System.AttributeTargets.* brings all enum members into unqualified value scope, and an individual import such as import System.AttributeTargets.Delegate brings that single member into scope. An individual enum-member import is deliberate and follows the existing precedence rules for specific imports.

Extension methods defined on imported types are also brought into scope. This enables consuming .NET helpers such as System.Linq.Enumerable.Where or System.MemoryExtensions.AsSpan directly from Raven source:

import System.Collections.Generic.*
import System.Linq.*

let odds = List<int>()
odds.Add(1)
odds.Add(3)
let filtered = odds.Where(value => value % 2 == 1)

Raven exposes Where as an instance-style call even though .NET declares it as a static extension method. The receiver becomes the method's first argument.

Import order and scope

Import directives appear at the beginning of a compilation unit or namespace and simply make existing namespaces or types available. They do not introduce new names. To bind a custom name, use an alias directive. All imports for a given scope must come before any alias directives or member declarations. Placing an import directive after an alias or member in the same scope is a compile-time error (RAV1005).

An import belongs to the scope that syntactically contains it. Compilation-unit imports are visible to declarations in that compilation unit, including declarations inside a file-scoped namespace. Imports inside a block-scoped namespace are visible only inside that namespace declaration and its nested declarations; they are not visible to sibling namespace declarations. File-scoped namespace imports participate in the same file namespace context as compilation-unit imports. For clarity, style guidance recommends placing imports and aliases after the file-scoped namespace declaration, but imports before the file-scoped namespace declaration are also part of the file's namespace context.

Project-wide imports

Project builds include a generated prelude source file. The prelude uses a top-level global block. Global blocks are top-level constructs whose supported import entries are processed early during binding as compilation-wide imports:

global {
    import System.*
    import System.Collections.*
    import System.Collections.Generic.*
    import System.IO.*
    import System.Linq.*
    import System.Net.Http.*
    import System.Threading.*
    import System.Threading.Tasks.*
    import System.Result.*
    import System.Option.*
}

The imports in this generated file participate in ordinary import lookup for the project. Global imports are hoisted, but they still use ordinary namespace and type import binding rules. Namespace imports are the preferred project-file shape. Type-scope imports such as System.Result.* and direct nested-case imports such as System.Result.Ok require the imported type or nested type to be available to the compilation; they are supported, but are best reserved for stable library/prelude cases.

The standard Result and Option case imports make Ok, Error, Some, and None available by simple name in project code. User-defined union cases are not automatically available as simple names; use a qualified case name, target-typed member syntax, or an explicit wildcard type import such as import FridgeError.*.

Project SDKs supply implicit imports as evaluated MSBuild Import items. Projects can disable SDK-supplied items with ImplicitImports=disable, add their own items, or remove an item contributed by the SDK. ImplicitUsings and GeneratePreludeImports remain compatibility aliases when ImplicitImports is not set.

<PropertyGroup>
  <ImplicitImports>enable</ImplicitImports>
</PropertyGroup>

<ItemGroup>
  <Import Include="SuperheroApp.Models" />
  <Import Include="System.Console" Static="True" />
  <Import Include="System.DateTime" Alias="DT" />
  <Import Remove="System.Net.Http" />
</ItemGroup>

The first two included items generate import entries in the global prelude block; the alias item generates a project-wide alias from the generated prelude file. The removal uses ordinary MSBuild item semantics and can remove an import declared earlier by the selected SDK. Within an ItemGroup, Import is an item name rather than MSBuild's project-level <Import Project="..." /> element. Repeating an import that is already supplied globally is redundant and may be reported as a hidden diagnostic with an editor fix to remove the local import.

Aliases

The alias directive assigns an alternative name to a fully qualified namespace, type, static member, or to type expressions such as tuples.

alias IO = System.IO
alias SB = System.Text.StringBuilder
alias PrintLine = System.Console.WriteLine
alias Pair = (x: int, y: int)
alias Flag = bool
alias Text = string

let sb = SB()
PrintLine("Hi")
let tmp = IO.Path.GetTempPath()

Aliasing a method binds a specific overload. Multiple directives using the same alias name may appear to alias additional overloads, forming an overload set.

Predefined types may be aliased directly. The supported built-in alias targets are bool, char, int, long, float, double, string, and unit (spelled unit or ()). Raven has no void; the unit type is used instead (see implementation notes). If the alias target is invalid, the compiler emits diagnostic RAV2020.

Aliases require fully qualified names for namespaces, types, and members to avoid ambiguity; type expressions are written directly. Alias directives may appear at the top of a file or inside a namespace but must follow all import directives and precede any declarations or statements. An alias directive that appears after a member declaration produces diagnostic RAV1006.

Block-scoped namespaces

You may define multiple namespaces (including nested) in one file using block scopes:

// Members in the global namespace

namespace A1 {
    import System.*
    import System.IO.*

    // Members here

    namespace B1 {
        // Members here
    }
}

namespace A.B {
    // Members here
}

The outermost undeclared namespace is the global namespace.