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.