about summary refs log tree commit diff
diff options
context:
space:
mode:
authorsterni <sternenseemann@systemli.org>2021-03-04T21·20+0100
committersterni <sternenseemann@systemli.org>2021-03-05T11·07+0000
commit5ae1d3fd7b363b6aab71b94626055ea91675f90d (patch)
treebcf85f25c9391fe27bfa3df9d4638dab3ee9b26e
parentef40a8621fc4fdd6e197da52ab3f28b2b6e4c31d (diff)
feat(users/sterni/nix/flow): add switch conditional r/2269
switch would probably otherwise be called match, but has been renamed so
it isn't confused with string.match and the enum matching capabilities
yants has.

It implements the closest to pattern matching nix can come which is
still flexible enough to not be painful: Syntactically it works like
cond, but is given a value. Instead of booleans it checks passed
predicates or equality if simple values are passed. Both types of checks
can be mixed.

Change-Id: I40f000979cfd469316e15fd58d6c3a80312c1cc4
Reviewed-on: https://cl.tvl.fyi/c/depot/+/2589
Tested-by: BuildkiteCI
Reviewed-by: sterni <sternenseemann@systemli.org>
-rw-r--r--users/sterni/nix/flow/default.nix76
1 files changed, 53 insertions, 23 deletions
diff --git a/users/sterni/nix/flow/default.nix b/users/sterni/nix/flow/default.nix
index a9fe3c6735..b5783bd86d 100644
--- a/users/sterni/nix/flow/default.nix
+++ b/users/sterni/nix/flow/default.nix
@@ -6,47 +6,77 @@ let
     yants
     ;
 
+  inherit (depot.users.sterni.nix)
+    fun
+    ;
+
   # we must avoid evaluating any of the sublists
   # as they may contain conditions that throw
   condition = yants.restrict "condition"
     (ls: builtins.length ls == 2)
     (yants.list yants.any);
 
-  /* cond :: [ [ bool any ] ] -> any
-   *
-   * Like the common lisp macro: takes a list
-   * of two elemented lists whose first element
-   * is a boolean. The second element of the
-   * first list that has true as its first
-   * element is returned.
-   *
-   * Example:
-   *
-   * cond [
-   *   [ (builtins.isString true) 12 ]
-   *   [ (3 == 2) 13 ]
-   *   [ true 42 ]
-   * ]
-   *
-   * => 42
+  /* Like the common lisp macro: takes a list
+     of two elemented lists whose first element
+     is a boolean. The second element of the
+     first list that has true as its first
+     element is returned.
+
+     Type: [ [ bool a ] ] -> a
+
+     Example:
+
+     cond [
+       [ (builtins.isString true) 12 ]
+       [ (3 == 2) 13 ]
+       [ true 42 ]
+     ]
+
+     => 42
    */
-  cond = conds:
+  cond = conds: switch true conds;
+
+  /* Generic pattern match-ish construct for nix.
+     Takes a bunch of lists which are of length
+     two and checks the first element for either
+     a predicate or a value. The second value of
+     the first list which either has a value equal
+     to or a function that evaluates to true for
+     the given value.
+
+     Type: a -> [ [ (function | a) b ] ] -> b
+
+     Example:
+
+     switch "foo" [
+       [ "smol" "SMOL!!!" ]
+       [ (x: builtins.stringLength x <= 3) "smol-ish" ]
+       [ (fun.const true) "not smol" ]
+      ]
+
+      => "smol-ish"
+  */
+  switch = x: conds:
     if builtins.length conds == 0
-    then builtins.throw "cond: exhausted all conditions"
+    then builtins.throw "exhausted all conditions"
     else
       let
         c = condition (builtins.head conds);
+        s = builtins.head c;
+        b =
+          if builtins.isFunction s
+          then s x
+          else x == s;
       in
-        if builtins.head c
+        if b
         then builtins.elemAt c 1
-        else cond (builtins.tail conds);
+        else switch x (builtins.tail conds);
 
-  # TODO(sterni): condf or magic
-  # like <nixpkgs/pkgs/build-support/coq/extra-lib.nix>
 
 
 in {
   inherit
     cond
+    switch
     ;
 }