# 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.

let i = 12_345_678; // int
let hex = 0x1234_ABCD; // int
let long = 1_000_000_000_000_000L; // long

let hex_unsigned_long = 0x1234_5678__9ABC_DEF0_UL; // ulong

let b = 99b; // byte

# char

let c = 'c';
let u_macron = 'ū'

# floating point

let s = 123.456; // single
let t = 123.456E5; // single

let d = 123.456D; // double
let e = 123_456_789_000.0D // double

# string

let hello_world = "Hello World!";
let unicode = "ghūl programming language"

# 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.

let animals = ["frog", "bat", "elephant"]; // List[string]
let things = ["frog", 1234, 12.5] // List[object]
let lists = [[1, 2], [3, 4], [5, 6], [7, 7]] // List[List[int]]

# 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.

let path_with_id = (path = "/tmp/my-file.txt", id = 1234); // (path: string, id: int)

let path = path_with_id.path;
let id = path_with_id.id;

If tuple elements are not explicitly named, they are assigned names consisting of a back-tick followed by an index

let things = ("thing", 12.34); // (string, int) with element names `0 and `1

let name = things.`0;
let weight = things.`1;

# 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

let simple_add = (x: int, y: int) -> int => x + y;

# block body function literal

let complex_add = (x: int, y: int) -> int is
    let result = x + y;
    return result;
si;

# 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

let simple_add = (x: int, y: int) => x + y;

let complex_add = (x: int, y: int) is
    let result = x + y;
    return result;
si;

Argument types usually can be inferred if the function literal is being passed into a function.

let list = [1, 2, 3, 4, 5];

list | .filter(element => element < 3); // type of element is inferred to be int

# capturing and closure

In ghūl, function literals can capture and utilize values from their surrounding lexical scope, thereby forming closures. It's important to note that what are captured are the values of variables at the point of the function literal's creation, rather than the variables themselves. This distinction is crucial for understanding how closures work in ghūl.

When a function literal is constructed, it creates read-only snapshots of the values from the outer scopes that are referenced within it. These snapshots are immutable in the sense that the literal cannot alter the captured values. However, the immutability applies to the value's binding, not necessarily to the value itself. In ghūl, like in many .NET languages, a value could be a reference to an object. While the reference is immutable and remains constant throughout the lifetime of the function literal, the object it points to can still be mutable. This means that if the captured value is an object reference, the object's state can be modified either by the closure itself or by other code, but the reference held by the closure will always point to the same object.

Consider a simple closure that captures a loop variable:

let g = () => i;

In this case, g captures the value of i at the moment of g's creation. The variable i itself is not captured; only its value at a specific point in time is. Here is a more complete example:

// Define a list to hold the closures:
let closure_list = LIST[() -> int]();

// Iterate over an integer range:
for i in 1::10 do
    // Create a closure that captures the current value of i:
    let closure = () => i;

    // Add the closure to the list:
    closure_list.add(closure);
od

// Each closure captured the value of i at the time of its creation:
for closure in closure_list do
    write_line("Closure captured value: {closure()}");
od

If the captured value is a reference to an object, like in the following example:

let object_reference = SOME_OBJECT();
let closure = () => object_reference.some_property;

Then while closure cannot change what object_reference points to, it can interact with object_references's properties or methods, which can lead to changes in the state of the SOME_OBJECT object referenced by object_reference.

# arithmetic

Arithmetic expressions allow you to perform mathematical calculations using operators such as +, -, *, /, and %.

let sum = 10 + 5;           // Addition
let difference = 10 - 5;    // Subtraction
let product = 10 * 5;       // Multiplication
let quotient = 10 / 5;      // Division
let remainder = 10 % 3;     // Modulo (remainder)

# comparison

Comparison expressions allow you to compare values using operators such as ==, !=, <, >, <=, and >=.

let equal = 5 == 5;              // Equality
let not_equal = 5 != 10;         // Inequality
let less_than = 5 < 10;          // Less than
let greater_than = 10 > 5;       // Greater than
let less_than_or_equal = 5 <= 5; // Less than or equal to
let greater_than_or_equal = 10 >= 10; // Greater than or equal to

# short circuit logical

Logical expressions allow you to combine or negate boolean values using the && (logical AND), || (logical OR), and ! (logical NOT) operators.

let logical_and = true /\ false;    // Logical AND
let logical_or = true \/ false;     // Logical OR
let logical_not = !true;            // Logical NOT

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.

let max = if a > b then a else b fi;

# function call

Function call expressions allow you to invoke functions and methods by providing the necessary arguments.

let result = sum(10, 5);

# property access

Property access expressions allow you to access the properties of an object using the dot notation.

let length = "Hello".length;

# indexer

Indexer expressions allow you to access elements of an array or collection using square brackets.

let first_element = [1, 2, 3][0];

# constructor

Constructor expressions allow you to create new instances of classes or structs by invoking their constructors.

let point = POINT(10, 20);

# type cast

Type cast expressions allow you to explicitly convert a value from one type to another using the cast keyword.

let integer_value = cast int(3.14);

These are the main types of expressions in ghūl. They can be combined and nested to form more complex expressions and statements:

entry() is
    let x = 10;
    let y = 5;
    let sum = x + y;
    let product = x * y;
    let is_greater = x > y;

    if is_greater then
        IO.Std.write_line("x is greater than y");
    else
        IO.Std.write_line("x is not greater than y");
    fi

    let numbers = [1, 2, 3, 4, 5];
    let first_number = numbers[0];

    IO.Std.write_line("The first number is: {first_number}");
si