Sinks and reduce
A sink on a kind collects the elements that aspects give, and reduces them to one value.
of checks each element.
| Reduce | Value | Error |
|---|---|---|
list (default) | the elements in scope order | none |
set | the elements without duplicates | none |
one | the single element | 0 or 2+ elements |
merge | a deep attrset | two different leaves at one path |
entries | [ { from; value; } ]: each value with the scope that gave it | none |
| a function | [ { from; value; } ] -> { value; errors; } | the errors you give |
# A sink collects elements and reduces them. `of` checks each element.{ yen, lib, ... }:let inherit (yen.lib.reduce) list set one merge entries ; sorted = es: { value = lib.sort (a: b: a < b) (map (e: e.value) es); errors = [ ]; }; # your own reducein{ yen.kinds.host.sinks = { motd = { reduce = list; of = lib.types.str; }; ports = { reduce = set; of = lib.types.port; }; admin.reduce = one; nginx.reduce = merge; owners.reduce = entries; groups.reduce = sorted; }; # every sink of a host, read as args, to one flake output yen.aspects.report = { at = yen.kinds.host; out = { host, motd, ports, admin, nginx, owners, groups, ... }: { ${host.name} = { inherit motd ports admin nginx groups ; owners = map (e: e.from.kind) owners; }; }; };}Two aspects give elements:
{ yen, ... }:{ yen.aspects.base = { at = yen.kinds.host; motd = "welcome"; ports = 22; admin = "alice"; nginx.http.gzip = true; owners = "ops"; groups = "wheel"; }; yen.aspects.web = { at = yen.host.web; motd = "this host serves web"; ports = 22; # a duplicate: `set` keeps one nginx.http.servers.www.port = 80; # `merge` joins the attrsets groups = "audio"; };}And each sink reduces them:
{ admin = "alice"; groups = [ "audio" "wheel" ]; motd = [ "welcome" "this host serves web" ]; nginx = { http = { gzip = true; servers = { www = { port = 80; }; }; }; }; owners = [ "host" ]; ports = [ 22 ];}When a reduce fails
Section titled “When a reduce fails”A failed reduce is an error record. The other sinks still work:
# Each line breaks one sink rule.{ yen, ... }:{ yen.aspects.mistakes = { at = yen.host.web; admin = "bob"; # `one`: two admins ports = 70000; # `of`: not a port nginx.http.gzip = false; # `merge`: two different leaves at one path };}[ { class = "fixable"; code = "one"; error = "one: got 2 elements"; from = "fleet:main/host:web"; got = 2; path = [ ]; sink = "admin"; want = 1; } { class = "fixable"; code = "merge"; error = "merge conflict at http.gzip"; from = "fleet:main/host:web"; path = [ "http" "gzip" ]; sink = "nginx"; } { class = "fixable"; code = "error"; error = "sink ports: element of aspect mistakes from fleet:main/host:web fails `of`"; from = "fleet:main/host:web"; path = [ ]; }]Your own reduce
Section titled “Your own reduce”A reduce is any function from [ { from; value; } ] to { value; errors; }. Write one when the merge order
is the point: ranked layers, per-key strategies with provenance, or an export store where a scope can only write
its own entries. The engine sets from. An element cannot say where it comes from, so a scope cannot write for another.