expressions
Expressions in ghūl are constructs that evaluate to a value. They can be used to perform calculations, make comparisons, and combine values in various ways.
literals
integers
Integer literals consist of an optional radix (base), followed by a sequence of digits with optional underscores for readability, followed by a dot and a decimal fraction and/or exponent (for floating point numbers) and finally a type suffix.
char
floating point
string
array
Array literals are constructed from a comma separated list of element values enclosed in [ and ]. The array element type is inferred as the most specific type compatible with all elements (which may be object if no more specific ancestor type exists). The resulting array type is E[] where E is the inferred element type.
tuple
Tuple literals are constructed from a comma separated list of elements enclosed in ( and ). Each element can be a bare value or a named value, and each element can optionally specify a type. Where explicit types are omitted, element types will be inferred.
If tuple elements are not explicitly named, they are assigned names consisting of a back-tick followed by an index
function
Function literals are constructed from an parenthesized argument list, a return type, and a return expression or a function body. If there is only one argument, no parentheses are needed.
expression body function literal
block body function literal
type inference
Return type can usually be omitted provided it can be inferred from the type of the expression body or any values returned from the block body
Argument types usually can be inferred if the function literal is being passed into a function.
capturing and closure
A function literal can refer to identifiers from its surrounding lexical scope; those references form its closure. What the closure sees through each identifier depends on whether the local variable it refers to is mutable.
An immutable local (let) is captured by value at the point the function literal is constructed. The closure holds a snapshot of that value:
Inside a loop, each iteration produces its own local, so each closure captures its own value:
A mutable local (let followed by mut) is captured by reference: the closure shares one live variable with the outer scope and with any other closures over the same name. Assignments are visible in both directions.
counter = 15, peek() = 15
Mutability of the captured variable is independent of mutability of the value it holds. When the captured value is a reference to an object, the closure cannot reassign the variable to point at a different object, but it can still call methods or assign to properties on the object it currently refers to:
Here closure cannot make object_reference point somewhere else, but it can read and modify the state of the SOME_OBJECT it currently refers to.
arithmetic
Arithmetic expressions allow you to perform mathematical calculations using operators such as +, -, *, /, and %.
comparison
Comparison expressions allow you to compare values using operators such as ==, !=, <, >, <=, and >=.
short circuit logical
Logical expressions allow you to combine or negate boolean values using the && (logical AND), || (logical OR), and ! (logical NOT) operators.
Evaluation stops as soon as the result is known
conditional
Conditional expressions allow you to evaluate different expressions based on a condition using the if-then-else construct.
function call
Function call expressions allow you to invoke functions and methods by providing the necessary arguments.
property access
Property access expressions allow you to access the properties of an object using the dot notation.
indexer
Indexer expressions allow you to access elements of an array or collection using square brackets.
constructor
Constructor expressions allow you to create new instances of classes or structs by invoking their constructors.
type cast
Type cast expressions allow you to explicitly convert a value from one type to another using the cast keyword.
default value
A default expression evaluates to the default value of a type: null for reference types, the zero value for numeric and other value types.
default[T] pins the type explicitly. A bare default takes its type from the surrounding context — a typed let, an assignment, or a return:
let a = default initialises a local to its type's default value, where the type is inferred from how the local is later used.
These are the main types of expressions in ghūl. They can be combined and nested to form more complex expressions and statements:
x is greater than y The first number is: 1