Skip to content

In U, variables are immutable by default. Simply put, a variable is immutable when it can not be modified after being initialized. It allows to:

  • prevent memory errors by controlling variable access, sharing, and lifetime,
  • prevent race conditions in concurrent applications,
  • generate efficient executables.

Unlike immutable values that are freely optimized by U compiler, mutable values are abstractions of machine places, cannot be change and are more prone to errors.

See Immutability.

Variables

Variables are declared and initialized with a colon : and space around or comments. This is the only way to declare variables in U. This means that variables always have an initial value.

The initialization rule is <left side> ' : ' <right side>. The left side can be any value. The simpler case is to use a symbol like :myvar. See Identifier rules.

:number : 1 
:text : 'Congratulations!'
:name= : {:  ... }
agent.name= 'Bob'

:number and :text are symbols. But you can use other values on the left side too.

:forms : [:name, :age]

forms.first : 'Name'

\< "Name: " name

The variable's type is inferred from the value on the right hand side. To choose a different type, use type aperture:

:a : @int64 3 

Initialization vs assignment

In U, binding, initialization, and assignment are not the same:

  • binding: attach a value to a defined or undefined name
  • initialization: define an undefined variable
  • assignment: modify an already defined mutable variable

U ensures the correctness of your application by strictly controlling initialization with :, and assignment with :=.

# Immutable variable
:x : 1 # OK
:x : 2 # ERROR, 'x' already defined
x := 2 # ERROR, 'x' is immutable

# Mutable variable
::mut :y : 40 # OK
:y : 50 # ERROR, 'y' already defined
y := 50 # OK, 'y' is mutable
x and y are bound to their values 1, and 2.

Operator = is not for variable

The operator = is not used. It prevents confusion between initialization and assignement.

multiple Variable

initialization

:x : 1; :y : 2
(:a, :b) :.. (1, 2)

Initialization with the same value

(:x, :y) :.. 2

Since an initialization is an expression, it evaluates to a value:

(:x : 1) + 1
# Prints 2

An initialization expression has type of its value.

:x : 1
# (:x : 1) is an int

Mutable Variable

A mutable Value is then an abstraction of machine place (memory or register). Therefore it restricts compiler optimisations. However, in certain low level cases, mutable variable are a necessary feature.

To declare a mutable variable, use :!, or keyword ::mut. The compiler will carefully follow the variable lifetime to prevent errors.

::mut :v : 1
:w :! 1

v := 2 # 'v' <=> 2

w := 8 # 'w' <=> 8

The assignment operator := modify an already declared mutable variable. It can be seen as :(bind) followed by =(assign). It clearly reminds you that:

  • you must first bind a variable before using it,
  • you are re-binding a new value so you can be confident about what U will generate.

Variable Type

U infers variable's type by default, so you don't have to think about it. You can also add a type annotation:

@Position :v : 1
@int :v : 1

See Types

Variable Scope

U has scopes to manage variable lifetime. The scope of a variable is where its name is meaningful. Unlike Python, U uses curly braces {} to denote scopes.

For more details about why U is not indent-based, see Braces vs Indentation.

If you have ever used a programming language that uses curly braces, you'll feel right at home. If not, your current favorite IDE will easily indent U code. No need to add formatting overhead in the language.

Variable Locales and Globales

Unlike most other languages, U only allows local variable declarations in functions.

Global variables and constants are only allowed at the Pod level, if enabled in ugo.

Variable shadowing

Variable shadowing is when a variable has the same name as a variable declared in an outer scope:

:f : {:
  # Scope 0
  :x : 1  # Name: 'x', internal id: 'Scope-0-Var-0'
  {
    # Scope 1 
    # At this point no variable with name 'x' defined
    :x : 2  # Name 'x', internal id: 'Scope-1-Var-0'

    # At this point 'Scope-1-Var-0' shadows 'Scope-0-Var-0'
  }

  :x : 4 # Back to Scope 0, 
  # Error: name: 'x', internal id: Scope-0-Var-0 exists!
}

By default, U prevent variable shadowing, but you can allow it with ugo

Constants

As U value are immutable, defining constants is just binding a name to a literal:

:N : 5