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.

Each file may define a namespace:

namespace Foo

// Members here

Import directive

namespace Foo

import System.*
// or import System.Collections.*

// Members here

The wildcard may also be applied to a type name to bring its static members and nested types into scope:

import System.Math.*

val 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.*

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

The compiler treats Where as an instance-style invocation even though it is declared as a static C# extension method, inserting the receiver as the first argument when emitting IL.

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 builds also 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 files can disable the generated standard imports with GeneratePreludeImports=false. They can also add generated prelude imports with MSBuild Import items, including static type-scope imports and generated aliases:

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

The first two items generate import entries in the global prelude block; the alias item generates a project-wide alias from the generated prelude file. 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.

Alias directive

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

val sb = SB()
PrintLine("Hi")
val 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.

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.