tvl-depot/users/sterni/nix/flow/default.nix
sterni 5ae1d3fd7b feat(users/sterni/nix/flow): add switch conditional
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>
2021-03-05 11:07:41 +00:00

82 lines
1.7 KiB
Nix

{ depot, ... }:
let
inherit (depot.nix)
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);
/* 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: 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 "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 b
then builtins.elemAt c 1
else switch x (builtins.tail conds);
in {
inherit
cond
switch
;
}