-
Notifications
You must be signed in to change notification settings - Fork 1
Binding
A Binding represents a reactive read-only view over a value source. It allows you to observe changes and compose reactive expressions without introducing mutable state.
Bindings can be created from different sources:
local bind = require('astal').bind
local Variable = require('astal.variable')
local count = Variable.new(0)
local b = bind(count)The binding will always reflect the current value of the variable.
local bind = require('astal').bind
local battery = Battery.get_default()
local percentage = bind(battery, 'percentage')Bindings can also be created directly from Gio.Settings keys.
local Gio = require('lgi').require('Gio')
local bind = require('astal').bind
local settings = Gio.Settings {
schema_id = 'org.gnome.desktop.interface',
}
local theme = bind(settings, 'color-scheme') -- 'prefer-dark' | 'prefer-light' | 'default'Note
Values returned from Gio.Settings are automatically decoded from GVariant into native Lua types.
You can access deep properties using dot notation:
local icon_name = bind(network, 'wifi.icon-name')If any object in the chain changes, the binding will automatically re-evaluate.
Bindings are lazy. They compute their value only when requested.
print(b:get())If the binding is derived, the computation is executed on demand.
You can subscribe to updates:
local unsubscribe = b:subscribe(function(value)
print(value)
end)The callback receives the current computed value.
To stop listening:
unsubscribe()You can transform a binding using :as():
local text = bind(battery, 'percentage'):as(function(p)
return string.format('%d%%', p * 100)
end)Transformations are applied lazily and composed.
Bindings can be combined to form new reactive expressions.
Bindings support arithmetic and string operators:
local sum = bind(a) + bind(b)
local label = bind(name) .. " (" .. bind(count) .. ")"
local value = bind(x) * 2 + bind(y)All operators automatically create derived bindings.
Binding.derive({ bind(a), bind(b) }, function(x, y)
return x + y
end)Non-binding values are automatically converted into constant bindings when needed.
local value = bind(counter) + 10Here 10 is implicitly treated as a constant binding.
This allows seamless mixing of reactive and static values.
A Binding is always:
- lazy (computed only on demand)
- pure (no internal mutation)
- composable (can depend on other bindings)
- stateless (derived bindings do not store values)
- Bindings do not store state. Calling
:get()always recomputes the value if needed. - Each subscription is independent. A binding can be observed by multiple consumers safely.
- In operator expressions, non-bindings are automatically converted into constant bindings internally.
Conceptually, a derived binding behaves like:
function Binding.derive(deps, transform)
return {
get = function()
local values = {}
for i, d in ipairs(deps) do
values[i] = d:get()
end
return transform(table.unpack(values))
end,
subscribe = function(self, callback)
for _, d in ipairs(deps) do
d:subscribe(function()
callback(self:get())
end)
end
end
}
end