Skip to content

Namespaces

A namespace is a container that provides a scope for identifiers like variables, functions, and classes to prevent naming conflicts, especially in large projects or when using multiple libraries.

There are 2 types of namespaces:

  • Block-Scoped namespace
  • File-Scoped namespace

These are defined by saying writing namespace followed by its name and a block.

namespace Utilities {
class Vector {
}
fn something() {
}
}
# namespaces can use single-line blocks as well
namespace Constants => const X = 10

You can then use something inside the namespace by writing its name and dot notation

let vec = Utilities.Vector()
Utilities.something()

These namespaces can apply to entire files or allow you to divide files into different namespaces and store stuff in an organised fashion. Write namespace followed by its name and don’t provide a block.

Let’s imagine a file named utilities.cb (the name doesn’t matter.)

namespace Utilities
let x = 10
fn greet() => println("Hello")

You can then write using followed by the namespace name in main.cb:

using Utilities
Utilities.greet()
print(Utilities.x)

The using directive also works for Block-Scoped namespaces.

When creating a library or a module, its usually a good idea to keep your namespaces organised.

They tend to follow a certain pattern:

Where Library might be your main namespace, and Subspace is a sub-category inside that namespace that someone can access. For example:

If you have 2 files called vectors.cb and matrices.cb and you wanted to keep them both in Utilities but separate them, you can do the following:

vectors.cb
namespace Utilities::Vectors
class Vector2 {}
class Vector3 {}
matrices.cb
namespace Utilities::Matrices
class Matrix {}

Then simply import those namespaces in main.cb:

main.cb
using Utilities::Vectors
using Utilities::Matrices
# or import them both at once
using Utilities::{Vectors, Matrices}
let my_vector = Vectors.Vector2(1, 2)

Sometimes namespace names can get annoying to write or you may have multiple namespaces with the same name. To avoid these issues, use the as keyword to rename a namespace during import:

using Utilities::Matrices as Mat
let m = Mat.Matrix(1, 1, 1)

If you want to import all values in a namespace without needing to index into it by name, use the an asterisk (*) to import everything as a wildcard.

using Utilities::Vectors::*
# Can use Vector2 and Vector3 without needing to write `Vectors.` before them.
let x = Vector3(0, 0, 0)

Most languages (including Cranberry) have a standard library which contains a lot of built in functions. These include IO, Multithreading, file system, environment, etc.

Access the Cranberry standard library by writing

using Std

The Standard library has a lot of inner namespaces, such as

  • IO (terminal and colors)
  • Task (For multithreading and benchmarking)
  • Env (For executable arguments)
  • Math (Math related functions and constants)
  • FS (File system and directories)
  • Http (Sending requests and listening server)
  • Random (Pseudorandom number stuff)
  • JSON (Parsing and stringify-ing data to JSON)
  • Numerics (Internal Vector and Matrices creation functions for compatibility)
using Std::IO
let counter = 0
loop {
IO.clear()
println("Use the left and right arrow keys to change the counter")
println($"Counter: {counter}")
let key = IO.read_key(true) # `true` means don't send key to output
if key == "LeftArrow" => counter--
if key == "RightArrow" => counter++
}