Introduces the concept of a “tag”, a single-keyed attrset which annotates a nix value with a name. This can be used to implement tagged unions (by implying the list of possible tags is well-known), which has some overlap with how `nix.yants` does it. However, the more fascinating use-case is in concert with a so-called discriminator, `match` and hylomorphisms. The discriminator can take a nix value, and add tags to it based on some predicate. With `match`, we can then use that information to convert the discriminated values again. With `hylo`, we can combine both the “constructive” discriminator step with the “destructive” match step to recursively walk over a nix data structure (based on a description of how to recurse, e.g. through attrset values or list values), and then apply a transformation in one go. Change-Id: Ia335ca8b0881447fbbcb6bcd80f49feb835f1715 Reviewed-on: https://cl.tvl.fyi/c/depot/+/2434 Tested-by: BuildkiteCI Reviewed-by: sterni <sternenseemann@systemli.org>
		
			
				
	
	
		
			88 lines
		
	
	
	
		
			1.9 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
			
		
		
	
	
			88 lines
		
	
	
	
		
			1.9 KiB
		
	
	
	
		
			Nix
		
	
	
	
	
	
| { depot, lib, verifyTag, discr, discrDef, match, matchLam }:
 | |
| 
 | |
| let
 | |
|   inherit (depot.nix.runTestsuite)
 | |
|     runTestsuite
 | |
|     assertEq
 | |
|     it
 | |
|     ;
 | |
| 
 | |
|   isTag-test = it "checks whether something is a tag" [
 | |
|     (assertEq "is Tag"
 | |
|       (verifyTag { foo = "bar"; })
 | |
|       {
 | |
|         isTag = true;
 | |
|         name = "foo";
 | |
|         val = "bar";
 | |
|         errmsg = null;
 | |
|       })
 | |
|     (assertEq "is not Tag"
 | |
|       (removeAttrs (verifyTag { foo = "bar"; baz = 42; }) ["errmsg"])
 | |
|       {
 | |
|         isTag = false;
 | |
|         name = null;
 | |
|         val = null;
 | |
|       })
 | |
|   ];
 | |
| 
 | |
|   discr-test = it "can discr things" [
 | |
|     (assertEq "id"
 | |
|       (discr [
 | |
|         { a = lib.const true; }
 | |
|       ] "x")
 | |
|       { a = "x"; })
 | |
|     (assertEq "bools here, ints there"
 | |
|       (discr [
 | |
|         { bool = lib.isBool; }
 | |
|         { int = lib.isInt; }
 | |
|       ] 25)
 | |
|       { int = 25; })
 | |
|     (assertEq "bools here, ints there 2"
 | |
|       (discr [
 | |
|         { bool = lib.isBool; }
 | |
|         { int = lib.isInt; }
 | |
|       ] true)
 | |
|       { bool = true; })
 | |
|     (assertEq "fallback to default"
 | |
|       (discrDef "def" [
 | |
|         { bool = lib.isBool; }
 | |
|         { int = lib.isInt; }
 | |
|       ] "foo")
 | |
|       { def = "foo"; })
 | |
|   ];
 | |
| 
 | |
|   match-test = it "can match things" [
 | |
|     (assertEq "match example"
 | |
|       (let
 | |
|         success = { res = 42; };
 | |
|         failure = { err = "no answer"; };
 | |
|         matcher = {
 | |
|           res = i: i + 1;
 | |
|           err = _: 0;
 | |
|         };
 | |
|       in {
 | |
|         one = match success matcher;
 | |
|         two = match failure matcher;
 | |
|       })
 | |
|       { one = 43;
 | |
|         two = 0; })
 | |
|     (assertEq "matchLam & pipe"
 | |
|       (lib.pipe { foo = 42; } [
 | |
|         (matchLam {
 | |
|           foo = i: if i < 23 then { small = i; } else { big = i; };
 | |
|           bar = _: { small = 5; };
 | |
|         })
 | |
|         (matchLam {
 | |
|           small = i: "yay it was small";
 | |
|           big = i: "whoo it was big!";
 | |
|         })
 | |
|       ])
 | |
|       "whoo it was big!")
 | |
|   ];
 | |
| 
 | |
| in
 | |
|   runTestsuite "tag" [
 | |
|     isTag-test
 | |
|     discr-test
 | |
|     match-test
 | |
|   ]
 |