zignals is a signals library for Zig, with signals, derivations, and effects.
Warning
This is a toy project and is not meant for production use.
Add zignals as a dependency
zig fetch --save git+https://github.com/jmstevers/zignals
Then, add this to your build.zig
const zignals = b.dependency("zignals", .{
.target = target,
.optimize = optimize,
}).module("zignals");
exe.root_module.addImport("zignals", zignals);
const std = @import("std");
const zignals = @import("zignals");
var count: u32 = 0;
fn log(x: u32) void {
count += 1;
std.debug.print("x: {}\n", .{x});
}
fn addOne(x: u32) u32 {
return x + 1;
}
pub fn main() !void {
var system = zignals.System{};
const counter = system.signalT(u32, 0);
const increment = system.derived(addOne, .{counter});
_ = system.effect(log, .{increment});
try expectEqual(1, count);
try expectEqual(1, increment.get());
counter.set(1);
try expectEqual(2, count);
try expectEqual(2, increment.get());
}
After installing, start by importing the library.
const zignals = @import("zignals");
First, initialize a system that will keep track of update batches.
var system = zignals.System{};
Next, define a signal with an initial value. Signals are reactive state containers that notify dependents when their values change.
const counter = system.signalT(u32, 0);
Tip
To create a signal without specifying its type, you can create signals with automatic type inference using the signal
function.
const Foo = struct {
bar: []const u8,
};
const foo = Foo{};
const signal = system.signal(foo); // inferred as Signal(Foo)
With the signal created, you can create a derived value. Derivations are lazily computed and only update when you read them.
const increment = system.derived(addOne, .{counter});
This creates an effect that runs when dependencies change. Effects run immediately on creation and again whenever their dependencies update.
_ = system.effect(log, .{increment});
Tip
In this example, we discard the effect's return value. If you need to properly clean up an effect, you should capture the value and call deinit()
like this.
const effect = system.effect(log, .{increment});
defer effect.deinit();
The effect has already run once during initialization.
try expectEqual(1, count);
try expectEqual(1, increment.get());
When a signal updates, all derived values are marked dirty and effects that depend on the signal are automatically updated.
counter.set(1);
try expectEqual(2, count);
try expectEqual(2, increment.get());
To avoid heap allocation, zignals uses fixed size arrays to store dependencies and subscribers. To modify the maximum amounts add, a max_dependencies
or max_subscribers
field to the zignals
dependency in your build.zig
b.dependency("zignals", .{
.target = target,
.optimize = optimize,
.max_dependencies = 4, // defaults to 16
.max_subscribers = 32, // defaults to 16
})
- alien-signals-go for the topology and effect tests.
zignals uses the Apache-2.0 license.