15

Reactivity

Signals and effects with Àj??e

What is Àj??e?

Àj??e (Yoruba: "relationship") is Ifá-Lang's reactive programming system. Values automatically update when their dependencies change.

?? Signal

Reactive primitive that holds a value

?? Computed

Derived value that auto-updates

? Effect

Side-effect that runs on changes

Signals

A Signal is a reactive container for a value:

// Create a signal with initial value
ayanmo count = Signal.new(0);

// Read the value
Irosu.fo(count.get());  // 0

// Update the value
count.set(5);
Irosu.fo(count.get());  // 5

// Update based on current value
count.update(|n| n + 1);
Irosu.fo(count.get());  // 6

Computed Values

Computed values derive from signals and auto-update:

ayanmo price = Signal.new(100);
ayanmo quantity = Signal.new(3);

// Computed: automatically recalculates
ayanmo total = Computed.from(|| {
    padap? price.get() * quantity.get();
});

Irosu.fo(total.get());  // 300

// Change a dependency
quantity.set(5);
Irosu.fo(total.get());  // 500 (auto-updated!)

Effects

Effects run side-effects when dependencies change:

ayanmo name = Signal.new("Ifá");

// Effect: runs when 'name' changes
Effect.create(|| {
    Irosu.fo("Name changed to: " + name.get());
});

// Output: "Name changed to: Ifá" (runs immediately)

name.set("Orunmila");
// Output: "Name changed to: Orunmila" (runs again)

Subscriptions

ayanmo temperature = Signal.new(20);

// Subscribe to changes
temperature.subscribe(|new_val| {
    ti (new_val > 30) {
        Irosu.fo("?? High temperature: " + new_val);
    }
});

temperature.set(25);  // (no output, < 30)
temperature.set(35);  // "?? High temperature: 35"

Batching Updates

Group multiple updates to trigger effects once:

ayanmo a = Signal.new(1);
ayanmo b = Signal.new(2);

Effect.create(|| {
    Irosu.fo("Sum: " + (a.get() + b.get()));
});

// Without batch: effect runs twice
a.set(10);  // "Sum: 12"
b.set(20);  // "Sum: 30"

// With batch: effect runs once
batch(|| {
    a.set(100);
    b.set(200);
});  // "Sum: 300" (only once)

Practical Example: Form Validation

ayanmo username = Signal.new("");
ayanmo password = Signal.new("");

// Computed validation
ayanmo is_valid = Computed.from(|| {
    ayanmo u = username.get();
    ayanmo p = password.get();
    
    padap? Ika.len(u) >= 3 && Ika.len(p) >= 8;
});

// Effect: update UI on validation change
Effect.create(|| {
    ti (is_valid.get()) {
        Irosu.fo("? Form is valid");
    } bib?k? {
        Irosu.fo("? Form is invalid");
    }
});

username.set("adé");     // "? Form is invalid"
password.set("password123");  // "? Form is valid"

?? Congratulations!

You've completed the Tour of Ifá! You now understand:

What's Next?