lib: improve DAG library

Specifically,

- directly export `modules/lib/dag.nix` instead of renaming
  attributes,

- run through utilities to reuse code where possible,

- expose `lib.hm.dag.isEntry` and reuse it in
  `modules/lib/types-dag.nix`,

- reuse utilities through `lib` set instead of passing imports to
  functions, and

- eta reduction of `map`, `entryAnywhere`, `entryAfter` and
  `entryBefore`.
This commit is contained in:
rcerc 2022-01-13 12:21:32 -05:00 committed by Robert Helgesson
parent 30dda628bc
commit 4a724cb84c
No known key found for this signature in database
GPG key ID: 36BDAA14C2797E89
4 changed files with 50 additions and 79 deletions

View file

@ -4,39 +4,38 @@
#
# - not specific to strings, i.e., any payload is OK,
#
# - the addition of the function `dagEntryBefore` indicating a
# "wanted by" relationship.
# - the addition of the function `entryBefore` indicating a "wanted
# by" relationship.
{ lib }:
let inherit (lib) all any filterAttrs mapAttrs mapAttrsToList toposort;
in rec {
emptyDag = { };
let inherit (lib) all filterAttrs hm mapAttrs toposort;
in {
empty = { };
isEntry = e: e ? data && e ? after && e ? before;
isDag = dag:
let isEntry = e: (e ? data) && (e ? after) && (e ? before);
in builtins.isAttrs dag && all (x: x) (mapAttrsToList (n: isEntry) dag);
builtins.isAttrs dag && all hm.dag.isEntry (builtins.attrValues dag);
# Takes an attribute set containing entries built by
# dagEntryAnywhere, dagEntryAfter, and dagEntryBefore to a
# topologically sorted list of entries.
# Takes an attribute set containing entries built by entryAnywhere,
# entryAfter, and entryBefore to a topologically sorted list of
# entries.
#
# Internally this function uses the `toposort` function in
# `<nixpkgs/lib/lists.nix>` and its value is accordingly.
#
# Specifically, the result on success is
#
# { result = [{name = ?; data = ?;} …] }
# { result = [ { name = ?; data = ?; } … ] }
#
# For example
#
# nix-repl> dagTopoSort {
# a = dagEntryAnywhere "1";
# b = dagEntryAfter ["a" "c"] "2";
# c = dagEntryBefore ["d"] "3";
# d = dagEntryBefore ["e"] "4";
# e = dagEntryAnywhere "5";
# nix-repl> topoSort {
# a = entryAnywhere "1";
# b = entryAfter [ "a" "c" ] "2";
# c = entryBefore [ "d" ] "3";
# d = entryBefore [ "e" ] "4";
# e = entryAnywhere "5";
# } == {
# result = [
# { data = "1"; name = "a"; }
@ -51,66 +50,54 @@ in rec {
# And the result on error is
#
# {
# cycle = [ {after = ?; name = ?; data = ?} … ];
# loops = [ {after = ?; name = ?; data = ?} … ];
# cycle = [ { after = ?; name = ?; data = ? } … ];
# loops = [ { after = ?; name = ?; data = ? } … ];
# }
#
# For example
#
# nix-repl> dagTopoSort {
# a = dagEntryAnywhere "1";
# b = dagEntryAfter ["a" "c"] "2";
# c = dagEntryAfter ["d"] "3";
# d = dagEntryAfter ["b"] "4";
# e = dagEntryAnywhere "5";
# nix-repl> topoSort {
# a = entryAnywhere "1";
# b = entryAfter [ "a" "c" ] "2";
# c = entryAfter [ "d" ] "3";
# d = entryAfter [ "b" ] "4";
# e = entryAnywhere "5";
# } == {
# cycle = [
# { after = ["a" "c"]; data = "2"; name = "b"; }
# { after = ["d"]; data = "3"; name = "c"; }
# { after = ["b"]; data = "4"; name = "d"; }
# { after = [ "a" "c" ]; data = "2"; name = "b"; }
# { after = [ "d" ]; data = "3"; name = "c"; }
# { after = [ "b" ]; data = "4"; name = "d"; }
# ];
# loops = [
# { after = ["a" "c"]; data = "2"; name = "b"; }
# { after = [ "a" "c" ]; data = "2"; name = "b"; }
# ];
# } == {}
# }
# true
dagTopoSort = dag:
topoSort = dag:
let
dagBefore = dag: name:
mapAttrsToList (n: v: n)
(filterAttrs (n: v: any (a: a == name) v.before) dag);
builtins.attrNames
(filterAttrs (n: v: builtins.elem name v.before) dag);
normalizedDag = mapAttrs (n: v: {
name = n;
data = v.data;
after = v.after ++ dagBefore dag n;
}) dag;
before = a: b: any (c: a.name == c) b.after;
sorted = toposort before (mapAttrsToList (n: v: v) normalizedDag);
before = a: b: builtins.elem a.name b.after;
sorted = toposort before (builtins.attrValues normalizedDag);
in if sorted ? result then {
result = map (v: { inherit (v) name data; }) sorted.result;
} else
sorted;
# Applies a function to each element of the given DAG.
dagMap = f: dag: mapAttrs (n: v: v // { data = f n v.data; }) dag;
map = f: mapAttrs (n: v: v // { data = f n v.data; });
entryBetween = before: after: data: { inherit data before after; };
# Create a DAG entry with no particular dependency information.
dagEntryAnywhere = data: {
inherit data;
before = [ ];
after = [ ];
};
dagEntryBetween = before: after: data: { inherit data before after; };
dagEntryAfter = after: data: {
inherit data after;
before = [ ];
};
dagEntryBefore = before: data: {
inherit data before;
after = [ ];
};
entryAnywhere = hm.dag.entryBetween [ ] [ ];
entryAfter = hm.dag.entryBetween [ ];
entryBefore = before: hm.dag.entryBetween before [ ];
}

View file

@ -1,20 +1,7 @@
{ lib }:
rec {
dag =
let
d = import ./dag.nix { inherit lib; };
in
{
empty = d.emptyDag;
isDag = d.isDag;
topoSort = d.dagTopoSort;
map = d.dagMap;
entryAnywhere = d.dagEntryAnywhere;
entryBetween = d.dagEntryBetween;
entryAfter = d.dagEntryAfter;
entryBefore = d.dagEntryBefore;
};
dag = import ./dag.nix { inherit lib; };
assertions = import ./assertions.nix { inherit lib; };
@ -22,7 +9,7 @@ rec {
gvariant = import ./gvariant.nix { inherit lib; };
maintainers = import ./maintainers.nix;
strings = import ./strings.nix { inherit lib; };
types = import ./types.nix { inherit dag gvariant lib; };
types = import ./types.nix { inherit gvariant lib; };
shell = import ./shell.nix { inherit lib; };
zsh = import ./zsh.nix { inherit lib; };

View file

@ -1,13 +1,11 @@
{ dag, lib }:
{ lib }:
let
inherit (lib)
concatStringsSep defaultFunctor fixedWidthNumber imap1 isAttrs isList length
listToAttrs mapAttrs mkIf mkOrder mkOption mkOptionType nameValuePair
concatStringsSep defaultFunctor fixedWidthNumber hm imap1 isAttrs isList
length listToAttrs mapAttrs mkIf mkOrder mkOption mkOptionType nameValuePair
stringLength types warn;
isDagEntry = e: isAttrs e && (e ? data) && (e ? after) && (e ? before);
dagEntryOf = elemType:
let
submoduleType = types.submodule ({ name, ... }: {
@ -21,10 +19,10 @@ let
};
});
maybeConvert = def:
if isDagEntry def.value then
if hm.dag.isEntry def.value then
def.value
else
dag.entryAnywhere (if def ? priority then
hm.dag.entryAnywhere (if def ? priority then
mkOrder def.priority def.value
else
def.value);

View file

@ -1,5 +1,4 @@
{ lib, dag ? import ./dag.nix { inherit lib; }
, gvariant ? import ./gvariant.nix { inherit lib; } }:
{ lib, gvariant ? import ./gvariant.nix { inherit lib; } }:
let
inherit (lib)
@ -7,7 +6,7 @@ let
mergeAttrs mergeDefaultOption mergeOneOption mergeOptions mkOption
mkOptionType showFiles showOption types;
typesDag = import ./types-dag.nix { inherit dag lib; };
typesDag = import ./types-dag.nix { inherit lib; };
# Needed since the type is called gvariant and its merge attribute
# must refer back to the type.