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
and :text
are symbols. But you can use other values on the left side too.
The variable's type is inferred from the value on the right hand side. To choose a different type, use type aperture:
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¶
Initialization with the same value¶
Since an initialization is an expression, it evaluates to a value:
An initialization expression has type of its value.
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.
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:
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: