Expression and Scope
#
Expression and ScopeIn programming languages expression is a unit of code which returns a value. A function call with return value is an expression - it returns value; an integer (or bool or address) literal is also an expression - it has the value of its integer type and so on.
Expressions must be separated by semicolon*
* When you put a semicolon, 'internally' it's treated as ; (empty_expression)
. If you put any expression after semi, it will replace the empty one.
#
Empty expressionYou probably will never use it directly but empty expression in Rogue (in this way it's similar to Rust) is marked with empty parentheses:
Empty expression can be omitted as it's automatically put by VM.
#
Literal expressionsLook at the code below. Every line contains an expression which ends with a semicolon. Last line has three expressions separated by semicolons.
Good. You now know the simplest expressions there are. But why do we need them? And how to use it? It's time to know the let
keyword.
let
keyword#
Variables and To store expression value inside a variable (to pass it somewhere) you have a keyword let
(you've already seen it in primitives chapter). It creates a new variable either empty (undefined) or with value of expression.
Keyword
let
creates new variable inside current scope and optionally initializes this variable with value. Syntax for this expression is:let <VARIABLE> : <TYPE>;
orlet <VARIABLE> = <EXPRESSION>
.
After you've created and initialized variable you're able to modify or access its value by using a variable name. In example above variable a
was initialized in the end of function and was assigned a value of variable c
.
Equality sign
=
is an assignment operator. It assigns right-hand-side expression to the left-hand-side variable. Example:a = 10
- variablea
is assigned an integer value of10
.
#
Operators for integer typesRogue has a variety of operators to modify integer values. Here's a list:
Operator | Op | Types | |
---|---|---|---|
+ | sum | uint | Sum LHS and RHS |
- | sub | uint | Subtract RHS from LHS |
/ | div | uint | Divide LHS by RHS |
* | mul | uint | Multiply LHS times RHS |
% | mod | uint | Division remainder (LHS by RHS) |
<< | lshift | uint | Left bit shift LHS by RHS |
>> | rshift | uint | Right bit shift LHS by RHS |
& | and | uint | Bitwise AND |
^ | xor | uint | Bitwise XOR |
| | or | uint | Bitwise OR |
LHS - left-hand-side expression, RHS - right-hand-side expression; uint: u8, u64, u128.
#
Underscore "_" to mark unusedIn Rogue every variable must be used (otherwise your code won't compile), hence you can't initialize one and leave it untouched. Though you have one way to mark variable as intentionally unused - by using underscore _
.
You'll get an error if you try to compile this script:
The error:
Compiler message is pretty clear, so all you have to do in this case is put underscore instead:
#
ShadowingRogue allows you to define the same variable twice with one limitation - it still needs to be used. In the example above only second a
is used. The first one: let a = 1
is actually unused as on the next line we redefine a
while leaving the first one unused.
Though we still can make it work by using first one:
#
Block expressionA block is an expression; it's marked with curly braces - {}
. Block can contain other expressions (and other blocks). Function body (as you can see by already familiar curly-braces) is also a block in some sense (with few limitations).
#
Understanding scopesScope (as it's said in Wikipedia) is a region of code where binding is valid. In other words - it's a part of code in which variable exists. In Rogue scope is a block of code surrounded by curly braces - basically a block.
When defining block you actually define a scope.
As you can see from comments in this sample, scopes are defined by blocks (or functions), they can be nested and there's no limit to how many scopes you can define.
#
Variable lifetime and visibilityKeyword let creates a variable - you already know that. Though you probably don't know that defined variable will live only inside the scope where it's defined (hence inside nested scopes); simply put - it's unaccessible outside its scope and dies right after this scope's end.
Variable lives only within scope (or block) where it's defined. When its scope ends, the variable dies.
#
Block return valuesIn the previous part you've learned that block is an expression but we didn't cover why it is an expression and what is the block's return value.
Block can return a value, it's the value of the last expression inside this block if it's not followed by semicolon
May sound hard, so We'll give you few examples:
Last expression in scope (without semicolon) is the return value of this scope.
#
SummaryTo summarise:
- Every expression must end with semicolon unless it's the return value of block;
- Keyword
let
creates new variable with value or right-hand-side expression which lives as long as the scope in which it's been created; - Block is an expression that may or may not have return value.