Variables
By default, variables in Mita are immutable, which helps us optimize the generated code. However, you can always choose to make variables mutable if you need it. When a variable is immutable, its value has to be set during declaration and cannot be changed afterwards. The following code snippet illustrates the idea (we’ve omitted the package and import statements for brevity):
fn main() {
let x = 42;
println(`The value of x is ${x}`);
x = 64;
println(`The value of x is now ${x}`);
}
When you save this code, you will see an error message Assignment to constant not allowed.
at the x = 64
statement.
That’s the compiler enforcing the immutability of x
.
To make variables mutable, use the var
keyword. Then, you can re-assign a value to the variable after it was declared.
For example
var x = 42;
x = 64;
does not produce any compiler errors, unlike the example above. Because we wrote var
instead of let
, x
became mutable and thus could be re-assigned to a different value.
Tip: be immutable where you can
Using mutable variables instead of immutable ones can lead to less efficient code being generated. Thus, as a rule of thumb use immutable variables where you can.
Types and Type Inference
Until now we have never had to write the type of the variables we declared. Mita supports type inference so that you, the developer, does not have to tell the compiler things it already knows. However, there are times when we are unable to infer the type of a variable, for example when it is not initialized, or the compiler gets the type wrong. In those cases you can explicitly tell the compiler what type a variable should have. For example:
let x : uint8 = 20; // x is forced to be uint8, compared to the otherwise inferred int32
var y : int16; // y does not have an explicit initialization, so we need to explicitly denote the type.
This syntax for specifying types is consistent throughout the language. You can use it to specify the types of function parameters, the return type of a function or the types of structure members.
Scoping and Shadowing
In Mita variables are block scoped and there is no shadowing within the same block. This is unlike languages like Rust or Haskell, for example, where one would be able to re-use the same name in multiple variable declarations.
The following is not legal Mita code, the compiler will complain that it Cannot redeclare variable 'x'
.
fn main() {
let x = 42;
let x = x * 2; // Compiler error: Cannot redeclare variable 'x'
let x = x * 3; // Compiler error: Cannot redeclare variable 'x'
}
However, in a new block one can shadow a variable declared outside that block. The following code, for example, works just fine as it takes the x
declared outside the if block, multiplies it by two and stores it in a new immutable variable also named x.
fn main() {
let x = 42;
if(true) {
let x = x * 2;
println(`x in block equals ${x}`); // Output: x in block equals 84
}
println(`x outside block equals ${x}`); // Output: x outside block equals 42
}
Primitive Data Types
Previously we have seen that Mita is a statically typed language, meaning that every expression in Mita has a data type, fixed at compile time. We distinguish between primitive and complex data types. Primitive data types consist of a single value, such as a number or a flag (other languages call those scalar data types). Mita has three groups of primitive data types: integer, float and boolean.
Integer
An integer is a “whole number”, i.e. one without a fraction. We already used integers in the previous section, specifically the uint8
and int16
types.
All integer types prefixed with a u
are unsigned types, meaning that they store positive values only but have a wider range than their signed counterparts.
The number at the end of the type indicates their width in bits, that is how large a number they can store.
Width | Signed | Unsigned |
---|---|---|
8 bit | int8 |
uint8 |
16 bit | int16 |
uint16 |
32 bit | int32 |
uint32 |
Each signed type can store values from -2(n-1) to 2(n-1) - 1, unsigned types can store values ranging from 0 to 2n - 1, where n
is the width of the data type. For example, uint8
can store integers up to 28-1 = 255.
You can write integer literals either in decimal form (e.g. 42
) or in hexadecimal form (0xFF
). If you don’t specify a type explicitly the compiler will infer the concrete data type based on first use. For example:
fn addOne(x : int16) {
return x + 1;
}
fn main() {
let y = addOne(42); // 42 is infered as int16
}
Floating Point
Mita supports a 32 bit wide single precision float
datatype and a 64 bit wide double precision double
floating point number type.
By default floating point literals (e.g. 1.0
) are inferred as single precision float
.
Boolean
Like in most other programming languages Mita supports a boolean type with two possible values: true
and false
. The boolean type in Mita is called bool
. For example:
fn main() {
let isTrue = true;
let falseByDefault : bool; // with explicit type, bool defaults to false.
if(isTrue) {
println("It is true!");
}
}
Operations on Primitive Types
Mita supports a range of operations on primitive values. These operations are supported on operands of the same type, or compatible smaller types (e.g. int32 + int16
). No worries though, the compiler will tell you what’s allowed and what isn’t.
- multiplication:
*
- addition:
+
- subtraction:
-
- division:
/
- bit-wise negation:
~
- bit-shift left:
<<
- bit-shift right:
>>
- logical negation (bool only):
!
Complex Types
Complex data types (such as strings or structures) need more elaboration than a single section could encompass. Head over to the types section to find out more.
Control Structures
Code can branch using if statements
let foo = 10;
if(foo >= 15) {
println("That's high");
} else if(foo >= 10) {
println("That's less high");
} else {
println("That's not high");
}
If-conditions must be boolean
if(foo) { } /* compiler error: Incompatible types int32 and boolean. */
Curly brackets for blocks are required
if(true) printf("Foo"); /* syntax error: Missing { */
Traditional for loops are supported
for(var i = 0; i < 10; i++) { }
Traditional while loops are supported
while(true) { }
Do-while loops are supported
do { } while(true)