Skip to main content
Version: 🚧 Alpha 🚧

Attributes and Actions

Dot Notation​

Reading an attribute​

counter c <- counter(label: "clicks", value: 0, step: 1);
int val <- c.value; // val = 0

Writing an attribute​

c.value <- 42; // direct write
c.step <- 5; // change the step size

Note: Objects are NOT agents. They have no reflex, no schedule, no population. All state changes must be driven by explicit calls from the outside — either direct attribute assignment or via actions.

Calling Actions​

Actions that return a value​

int val <- c.increment(); // val = 43 (value becomes 45 after +step=5*1=5... wait, step is 5, so increment adds 5)
// After c.value <- 42, c.increment() adds step(5) → value becomes 47

Actions that modify state​

c.reset(); // sets value back to 0, no meaningful return
write c.to_string(); // clicks = 0 (step=5)

Chained Action Calls​

Use the return value directly as an operand:

bool big <- c.increment() > 50;
// This calls increment() which returns the new value,
// then compares it to 50 — all in one expression.

Passing Objects as Arguments​

Actions can take object arguments — the type is declared in the signature:

class counter {
int value <- 0;

bool same_value_as(counter other) {
return value = other.value;
}
}

counter c <- counter(value: 10);
counter d <- counter(value: 20);
c.same_value_as(d); // false after both have value 10 → true after reset

Object Comparison​

Two objects of the same type can be compared using the = operator. In the counter example, same_value_as checks if the value attributes match.

Aggregate Operations Over Lists of Objects​

Objects stored in lists can be processed using GAML's collection operations:

list<counter> counters <- [];
loop i from: 1 to: 5 {
counters <+ counter(label: "c" + i, value: rnd(10), step: 1);
}

// Collect an attribute from every object
write "Counters: " + (counters collect each.to_string());

// Compute aggregate values
int max_val <- max(counters collect each.value);
float mean_val <- mean(counters collect each.value);
write "Max value: " + max_val;
write "Mean value: " + (mean_val with_precision 2);

// Iterate and mutate
loop cnt over: counters {
if float(cnt.value) < mean_val {
cnt.increment();
}
}

Example: Bank Account​

class bank_account {
string owner <- "unknown";
float balance <- 0.0;
float min_balance <- 0.0;

bool deposit(float amount) {
if amount <= 0.0 { return false; }
balance <- balance + amount;
return true;
}

bool withdraw(float amount) {
if amount <= 0.0 { return false; }
if balance - amount < min_balance { return false; }
balance <- balance - amount;
return true;
}

bool transfer(float amount, bank_account target) {
if withdraw(amount) {
bool ok <- target.deposit(amount);
return ok;
}
return false;
}

string to_string() {
return owner + ": balance=" + (balance with_precision 2);
}
}

global {
init {
bank_account alice <- bank_account(owner: "Alice", balance: 500.0);
bank_account bob <- bank_account(owner: "Bob", balance: 50.0);

bool ok1 <- alice.deposit(200.0); // true
bool ok2 <- alice.withdraw(100.0); // true
bool ok4 <- alice.transfer(300.0, bob); // true (Alice:200, Bob:350)

bool ok5 <- alice.transfer(999.0, bob); // false (insufficient funds)
}
}

Key Takeaways​

OperationSyntax
Read attributeobj.attr
Write attributeobj.attr <- value
Call actionobj.action(args)
Chain action resultsbool ok <- obj.action() > threshold
Pass object as argumentobj.method(other_object)
Collect attributelist collect each.attr