Signals and effects with Àj??e
Àj??e (Yoruba: "relationship") is Ifá-Lang's reactive programming system. Values automatically update when their dependencies change.
Reactive primitive that holds a value
Derived value that auto-updates
Side-effect that runs on changes
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 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 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)
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"
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)
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"
You've completed the Tour of Ifá! You now understand: