Variables

Like in mathematics and many programming languages, N has variables that allow you to store values of any type under a variable name.

In N, all variables are constants. This means that their value, once set, will never change. This makes N programs reliable because you can trust that a function can be run multiple times without behaving differently, and this allows N programs to be multi-threaded without running into issues with race conditions.

let statements

To create a variable, you can use the let statement. The keyword let is followed by the variable name, a colon (:), the type of the variable (called the type annotation), an equals sign (=), and the expression whose evaluated result should be stored in the variable. In the following example, we set a variable named name to the string "Billy", and we indicate that name is of the str type.

let name: str = 'Billy'

In many cases, however, the type can be inferred, so the type annotation can be omitted. The above code can be simplified to the following:

let name = 'Billy'

You can use a definite pattern in a let statement to extract values from various types of values, such as records and tuples.

let { name, age: yearsSinceBirth } = {
  name: "Billy",
  age: 36,
}

assert value (name, yearsSinceBirth) == ("Billy", 36)

Learn more about definite patterns at Patterns.

Exporting variables

In a module, you can export a variable by adding the pub keyword after let. For example, the following exports a variable named wow from the module, making it accessible by other modules.

// a.n

let pub a = "aaaaaa"
// b.n

let aModule = imp "./a.n"

assert value aModule.a == "aaaaaa"

Learn more about exporting values at Importing .n files.

Other ways of creating variables

let statements aren't the only way variables can be defined.

Function arguments can also store the argument values given to the function by a function call. Definite patterns can also be used in function arguments. Notably, however, type annotations are required for function arguments.

For example, in the following example, name and age are usable like variables inside the displayPerson function.

let displayPerson = [(name, age): (str, int)] -> str {
  return name + ", a " + intInBase10(age) + "-year-old neckbeard"
}

assert value displayPerson(("Billy", 36)) == "Billy, a 36-year-old neckbeard"

Variables are also created by conditional patterns in if let statements and expressions. In the following example, text is usable like a variable containing a string.

if let yes(text) = yes("hello") {
  assert value text == "hello"
}

Mutability

All variables, by default, are immutable in N, though in previous versions this was not the case as one could use the var keyword to change the value of any variable.

Now, to change the value of a variable you have to use the mut keyword after pub or if it is not there, let. This will allow you to directly reassign the value of the variable.

let mut val = 1;

val = val + 1
assert value val == 2

You do not have to only use = you are also able to use /=, *=, +=, -=, |=, &=, and %=. These are all expanded out internally.

Mutability and async

Mutability in async functions can lead to race conditions when implementing parallelism. N will warn you about this but will not explicitly cause and error. If you want to mutate a value without the chance of race conditions, use the mutex library.