at 17.09-beta 14 kB view raw
1# Operations on attribute sets. 2 3let 4 inherit (builtins) head tail length; 5 inherit (import ./trivial.nix) and or; 6 inherit (import ./default.nix) fold; 7 inherit (import ./strings.nix) concatStringsSep; 8 inherit (import ./lists.nix) concatMap concatLists all deepSeqList; 9in 10 11rec { 12 inherit (builtins) attrNames listToAttrs hasAttr isAttrs getAttr; 13 14 15 /* Return an attribute from nested attribute sets. 16 17 Example: 18 x = { a = { b = 3; }; } 19 attrByPath ["a" "b"] 6 x 20 => 3 21 attrByPath ["z" "z"] 6 x 22 => 6 23 */ 24 attrByPath = attrPath: default: e: 25 let attr = head attrPath; 26 in 27 if attrPath == [] then e 28 else if e ? ${attr} 29 then attrByPath (tail attrPath) default e.${attr} 30 else default; 31 32 /* Return if an attribute from nested attribute set exists. 33 34 Example: 35 x = { a = { b = 3; }; } 36 hasAttrByPath ["a" "b"] x 37 => true 38 hasAttrByPath ["z" "z"] x 39 => false 40 41 */ 42 hasAttrByPath = attrPath: e: 43 let attr = head attrPath; 44 in 45 if attrPath == [] then true 46 else if e ? ${attr} 47 then hasAttrByPath (tail attrPath) e.${attr} 48 else false; 49 50 51 /* Return nested attribute set in which an attribute is set. 52 53 Example: 54 setAttrByPath ["a" "b"] 3 55 => { a = { b = 3; }; } 56 */ 57 setAttrByPath = attrPath: value: 58 if attrPath == [] then value 59 else listToAttrs 60 [ { name = head attrPath; value = setAttrByPath (tail attrPath) value; } ]; 61 62 63 /* Like `getAttrPath' without a default value. If it doesn't find the 64 path it will throw. 65 66 Example: 67 x = { a = { b = 3; }; } 68 getAttrFromPath ["a" "b"] x 69 => 3 70 getAttrFromPath ["z" "z"] x 71 => error: cannot find attribute `z.z' 72 */ 73 getAttrFromPath = attrPath: set: 74 let errorMsg = "cannot find attribute `" + concatStringsSep "." attrPath + "'"; 75 in attrByPath attrPath (abort errorMsg) set; 76 77 78 /* Return the specified attributes from a set. 79 80 Example: 81 attrVals ["a" "b" "c"] as 82 => [as.a as.b as.c] 83 */ 84 attrVals = nameList: set: map (x: set.${x}) nameList; 85 86 87 /* Return the values of all attributes in the given set, sorted by 88 attribute name. 89 90 Example: 91 attrValues {c = 3; a = 1; b = 2;} 92 => [1 2 3] 93 */ 94 attrValues = builtins.attrValues or (attrs: attrVals (attrNames attrs) attrs); 95 96 97 /* Collect each attribute named `attr' from a list of attribute 98 sets. Sets that don't contain the named attribute are ignored. 99 100 Example: 101 catAttrs "a" [{a = 1;} {b = 0;} {a = 2;}] 102 => [1 2] 103 */ 104 catAttrs = builtins.catAttrs or 105 (attr: l: concatLists (map (s: if s ? ${attr} then [s.${attr}] else []) l)); 106 107 108 /* Filter an attribute set by removing all attributes for which the 109 given predicate return false. 110 111 Example: 112 filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; } 113 => { foo = 1; } 114 */ 115 filterAttrs = pred: set: 116 listToAttrs (concatMap (name: let v = set.${name}; in if pred name v then [(nameValuePair name v)] else []) (attrNames set)); 117 118 119 /* Filter an attribute set recursively by removing all attributes for 120 which the given predicate return false. 121 122 Example: 123 filterAttrsRecursive (n: v: v != null) { foo = { bar = null; }; } 124 => { foo = {}; } 125 */ 126 filterAttrsRecursive = pred: set: 127 listToAttrs ( 128 concatMap (name: 129 let v = set.${name}; in 130 if pred name v then [ 131 (nameValuePair name ( 132 if isAttrs v then filterAttrsRecursive pred v 133 else v 134 )) 135 ] else [] 136 ) (attrNames set) 137 ); 138 139 /* Apply fold functions to values grouped by key. 140 141 Example: 142 foldAttrs (n: a: [n] ++ a) [] [{ a = 2; } { a = 3; }] 143 => { a = [ 2 3 ]; } 144 */ 145 foldAttrs = op: nul: list_of_attrs: 146 fold (n: a: 147 fold (name: o: 148 o // (listToAttrs [{inherit name; value = op n.${name} (a.${name} or nul); }]) 149 ) a (attrNames n) 150 ) {} list_of_attrs; 151 152 153 /* Recursively collect sets that verify a given predicate named `pred' 154 from the set `attrs'. The recursion is stopped when the predicate is 155 verified. 156 157 Type: 158 collect :: 159 (AttrSet -> Bool) -> AttrSet -> [x] 160 161 Example: 162 collect isList { a = { b = ["b"]; }; c = [1]; } 163 => [["b"] [1]] 164 165 collect (x: x ? outPath) 166 { a = { outPath = "a/"; }; b = { outPath = "b/"; }; } 167 => [{ outPath = "a/"; } { outPath = "b/"; }] 168 */ 169 collect = pred: attrs: 170 if pred attrs then 171 [ attrs ] 172 else if isAttrs attrs then 173 concatMap (collect pred) (attrValues attrs) 174 else 175 []; 176 177 178 /* Utility function that creates a {name, value} pair as expected by 179 builtins.listToAttrs. 180 181 Example: 182 nameValuePair "some" 6 183 => { name = "some"; value = 6; } 184 */ 185 nameValuePair = name: value: { inherit name value; }; 186 187 188 /* Apply a function to each element in an attribute set. The 189 function takes two arguments --- the attribute name and its value 190 --- and returns the new value for the attribute. The result is a 191 new attribute set. 192 193 Example: 194 mapAttrs (name: value: name + "-" + value) 195 { x = "foo"; y = "bar"; } 196 => { x = "x-foo"; y = "y-bar"; } 197 */ 198 mapAttrs = f: set: 199 listToAttrs (map (attr: { name = attr; value = f attr set.${attr}; }) (attrNames set)); 200 201 202 /* Like `mapAttrs', but allows the name of each attribute to be 203 changed in addition to the value. The applied function should 204 return both the new name and value as a `nameValuePair'. 205 206 Example: 207 mapAttrs' (name: value: nameValuePair ("foo_" + name) ("bar-" + value)) 208 { x = "a"; y = "b"; } 209 => { foo_x = "bar-a"; foo_y = "bar-b"; } 210 */ 211 mapAttrs' = f: set: 212 listToAttrs (map (attr: f attr set.${attr}) (attrNames set)); 213 214 215 /* Call a function for each attribute in the given set and return 216 the result in a list. 217 218 Example: 219 mapAttrsToList (name: value: name + value) 220 { x = "a"; y = "b"; } 221 => [ "xa" "yb" ] 222 */ 223 mapAttrsToList = f: attrs: 224 map (name: f name attrs.${name}) (attrNames attrs); 225 226 227 /* Like `mapAttrs', except that it recursively applies itself to 228 attribute sets. Also, the first argument of the argument 229 function is a *list* of the names of the containing attributes. 230 231 Type: 232 mapAttrsRecursive :: 233 ([String] -> a -> b) -> AttrSet -> AttrSet 234 235 Example: 236 mapAttrsRecursive (path: value: concatStringsSep "-" (path ++ [value])) 237 { n = { a = "A"; m = { b = "B"; c = "C"; }; }; d = "D"; } 238 => { n = { a = "n-a-A"; m = { b = "n-m-b-B"; c = "n-m-c-C"; }; }; d = "d-D"; } 239 */ 240 mapAttrsRecursive = mapAttrsRecursiveCond (as: true); 241 242 243 /* Like `mapAttrsRecursive', but it takes an additional predicate 244 function that tells it whether to recursive into an attribute 245 set. If it returns false, `mapAttrsRecursiveCond' does not 246 recurse, but does apply the map function. It is returns true, it 247 does recurse, and does not apply the map function. 248 249 Type: 250 mapAttrsRecursiveCond :: 251 (AttrSet -> Bool) -> ([String] -> a -> b) -> AttrSet -> AttrSet 252 253 Example: 254 # To prevent recursing into derivations (which are attribute 255 # sets with the attribute "type" equal to "derivation"): 256 mapAttrsRecursiveCond 257 (as: !(as ? "type" && as.type == "derivation")) 258 (x: ... do something ...) 259 attrs 260 */ 261 mapAttrsRecursiveCond = cond: f: set: 262 let 263 recurse = path: set: 264 let 265 g = 266 name: value: 267 if isAttrs value && cond value 268 then recurse (path ++ [name]) value 269 else f (path ++ [name]) value; 270 in mapAttrs g set; 271 in recurse [] set; 272 273 274 /* Generate an attribute set by mapping a function over a list of 275 attribute names. 276 277 Example: 278 genAttrs [ "foo" "bar" ] (name: "x_" + name) 279 => { foo = "x_foo"; bar = "x_bar"; } 280 */ 281 genAttrs = names: f: 282 listToAttrs (map (n: nameValuePair n (f n)) names); 283 284 285 /* Check whether the argument is a derivation. Any set with 286 { type = "derivation"; } counts as a derivation. 287 288 Example: 289 nixpkgs = import <nixpkgs> {} 290 isDerivation nixpkgs.ruby 291 => true 292 isDerivation "foobar" 293 => false 294 */ 295 isDerivation = x: isAttrs x && x ? type && x.type == "derivation"; 296 297 /* Converts a store path to a fake derivation. */ 298 toDerivation = path: 299 let 300 path' = builtins.storePath path; 301 res = 302 { type = "derivation"; 303 name = builtins.unsafeDiscardStringContext (builtins.substring 33 (-1) (baseNameOf path')); 304 outPath = path'; 305 outputs = [ "out" ]; 306 out = res; 307 outputName = "out"; 308 }; 309 in res; 310 311 312 /* If `cond' is true, return the attribute set `as', 313 otherwise an empty attribute set. 314 315 Example: 316 optionalAttrs (true) { my = "set"; } 317 => { my = "set"; } 318 optionalAttrs (false) { my = "set"; } 319 => { } 320 */ 321 optionalAttrs = cond: as: if cond then as else {}; 322 323 324 /* Merge sets of attributes and use the function f to merge attributes 325 values. 326 327 Example: 328 zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}] 329 => { a = ["x" "y"]; } 330 */ 331 zipAttrsWithNames = names: f: sets: 332 listToAttrs (map (name: { 333 inherit name; 334 value = f name (catAttrs name sets); 335 }) names); 336 337 /* Implementation note: Common names appear multiple times in the list of 338 names, hopefully this does not affect the system because the maximal 339 laziness avoid computing twice the same expression and listToAttrs does 340 not care about duplicated attribute names. 341 342 Example: 343 zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}] 344 => { a = ["x" "y"]; b = ["z"] } 345 */ 346 zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets; 347 /* Like `zipAttrsWith' with `(name: values: value)' as the function. 348 349 Example: 350 zipAttrs [{a = "x";} {a = "y"; b = "z";}] 351 => { a = ["x" "y"]; b = ["z"] } 352 */ 353 zipAttrs = zipAttrsWith (name: values: values); 354 355 /* Does the same as the update operator '//' except that attributes are 356 merged until the given predicate is verified. The predicate should 357 accept 3 arguments which are the path to reach the attribute, a part of 358 the first attribute set and a part of the second attribute set. When 359 the predicate is verified, the value of the first attribute set is 360 replaced by the value of the second attribute set. 361 362 Example: 363 recursiveUpdateUntil (path: l: r: path == ["foo"]) { 364 # first attribute set 365 foo.bar = 1; 366 foo.baz = 2; 367 bar = 3; 368 } { 369 #second attribute set 370 foo.bar = 1; 371 foo.quz = 2; 372 baz = 4; 373 } 374 375 returns: { 376 foo.bar = 1; # 'foo.*' from the second set 377 foo.quz = 2; # 378 bar = 3; # 'bar' from the first set 379 baz = 4; # 'baz' from the second set 380 } 381 382 */ 383 recursiveUpdateUntil = pred: lhs: rhs: 384 let f = attrPath: 385 zipAttrsWith (n: values: 386 if tail values == [] 387 || pred attrPath (head (tail values)) (head values) then 388 head values 389 else 390 f (attrPath ++ [n]) values 391 ); 392 in f [] [rhs lhs]; 393 394 /* A recursive variant of the update operator //. The recursion 395 stops when one of the attribute values is not an attribute set, 396 in which case the right hand side value takes precedence over the 397 left hand side value. 398 399 Example: 400 recursiveUpdate { 401 boot.loader.grub.enable = true; 402 boot.loader.grub.device = "/dev/hda"; 403 } { 404 boot.loader.grub.device = ""; 405 } 406 407 returns: { 408 boot.loader.grub.enable = true; 409 boot.loader.grub.device = ""; 410 } 411 412 */ 413 recursiveUpdate = lhs: rhs: 414 recursiveUpdateUntil (path: lhs: rhs: 415 !(isAttrs lhs && isAttrs rhs) 416 ) lhs rhs; 417 418 /* Returns true if the pattern is contained in the set. False otherwise. 419 420 Example: 421 matchAttrs { cpu = {}; } { cpu = { bits = 64; }; } 422 => true 423 */ 424 matchAttrs = pattern: attrs: assert isAttrs pattern; 425 fold and true (attrValues (zipAttrsWithNames (attrNames pattern) (n: values: 426 let pat = head values; val = head (tail values); in 427 if length values == 1 then false 428 else if isAttrs pat then isAttrs val && matchAttrs pat val 429 else pat == val 430 ) [pattern attrs])); 431 432 /* Override only the attributes that are already present in the old set 433 useful for deep-overriding. 434 435 Example: 436 x = { a = { b = 4; c = 3; }; } 437 overrideExisting x { a = { b = 6; d = 2; }; } 438 => { a = { b = 6; d = 2; }; } 439 */ 440 overrideExisting = old: new: 441 old // listToAttrs (map (attr: nameValuePair attr (attrByPath [attr] old.${attr} new)) (attrNames old)); 442 443 /* Get a package output. 444 If no output is found, fallback to `.out` and then to the default. 445 446 Example: 447 getOutput "dev" pkgs.openssl 448 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" 449 */ 450 getOutput = output: pkg: 451 if pkg.outputUnspecified or false 452 then pkg.${output} or pkg.out or pkg 453 else pkg; 454 455 getBin = getOutput "bin"; 456 getLib = getOutput "lib"; 457 getDev = getOutput "dev"; 458 459 /* Pick the outputs of packages to place in buildInputs */ 460 chooseDevOutputs = drvs: builtins.map getDev drvs; 461 462 /*** deprecated stuff ***/ 463 464 zipWithNames = zipAttrsWithNames; 465 zip = builtins.trace 466 "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith; 467 468}