at 16.09-beta 14 kB view raw
1# Operations on attribute sets. 2 3with { 4 inherit (builtins) head tail length; 5 inherit (import ./trivial.nix) or; 6 inherit (import ./default.nix) fold; 7 inherit (import ./strings.nix) concatStringsSep; 8 inherit (import ./lists.nix) concatMap concatLists all deepSeqList; 9}; 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 recursivelly 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 path' = builtins.storePath path; in 300 { type = "derivation"; 301 name = builtins.unsafeDiscardStringContext (builtins.substring 33 (-1) (baseNameOf path')); 302 outPath = path'; 303 outputs = [ "out" ]; 304 }; 305 306 307 /* If `cond' is true, return the attribute set `as', 308 otherwise an empty attribute set. 309 310 Example: 311 optionalAttrs (true) { my = "set"; } 312 => { my = "set"; } 313 optionalAttrs (false) { my = "set"; } 314 => { } 315 */ 316 optionalAttrs = cond: as: if cond then as else {}; 317 318 319 /* Merge sets of attributes and use the function f to merge attributes 320 values. 321 322 Example: 323 zipAttrsWithNames ["a"] (name: vs: vs) [{a = "x";} {a = "y"; b = "z";}] 324 => { a = ["x" "y"]; } 325 */ 326 zipAttrsWithNames = names: f: sets: 327 listToAttrs (map (name: { 328 inherit name; 329 value = f name (catAttrs name sets); 330 }) names); 331 332 /* Implentation note: Common names appear multiple times in the list of 333 names, hopefully this does not affect the system because the maximal 334 laziness avoid computing twice the same expression and listToAttrs does 335 not care about duplicated attribute names. 336 337 Example: 338 zipAttrsWith (name: values: values) [{a = "x";} {a = "y"; b = "z";}] 339 => { a = ["x" "y"]; b = ["z"] } 340 */ 341 zipAttrsWith = f: sets: zipAttrsWithNames (concatMap attrNames sets) f sets; 342 /* Like `zipAttrsWith' with `(name: values: value)' as the function. 343 344 Example: 345 zipAttrs [{a = "x";} {a = "y"; b = "z";}] 346 => { a = ["x" "y"]; b = ["z"] } 347 */ 348 zipAttrs = zipAttrsWith (name: values: values); 349 350 /* Does the same as the update operator '//' except that attributes are 351 merged until the given pedicate is verified. The predicate should 352 accept 3 arguments which are the path to reach the attribute, a part of 353 the first attribute set and a part of the second attribute set. When 354 the predicate is verified, the value of the first attribute set is 355 replaced by the value of the second attribute set. 356 357 Example: 358 recursiveUpdateUntil (path: l: r: path == ["foo"]) { 359 # first attribute set 360 foo.bar = 1; 361 foo.baz = 2; 362 bar = 3; 363 } { 364 #second attribute set 365 foo.bar = 1; 366 foo.quz = 2; 367 baz = 4; 368 } 369 370 returns: { 371 foo.bar = 1; # 'foo.*' from the second set 372 foo.quz = 2; # 373 bar = 3; # 'bar' from the first set 374 baz = 4; # 'baz' from the second set 375 } 376 377 */ 378 recursiveUpdateUntil = pred: lhs: rhs: 379 let f = attrPath: 380 zipAttrsWith (n: values: 381 if tail values == [] 382 || pred attrPath (head (tail values)) (head values) then 383 head values 384 else 385 f (attrPath ++ [n]) values 386 ); 387 in f [] [rhs lhs]; 388 389 /* A recursive variant of the update operator //. The recusion 390 stops when one of the attribute values is not an attribute set, 391 in which case the right hand side value takes precedence over the 392 left hand side value. 393 394 Example: 395 recursiveUpdate { 396 boot.loader.grub.enable = true; 397 boot.loader.grub.device = "/dev/hda"; 398 } { 399 boot.loader.grub.device = ""; 400 } 401 402 returns: { 403 boot.loader.grub.enable = true; 404 boot.loader.grub.device = ""; 405 } 406 407 */ 408 recursiveUpdate = lhs: rhs: 409 recursiveUpdateUntil (path: lhs: rhs: 410 !(isAttrs lhs && isAttrs rhs) 411 ) lhs rhs; 412 413 /* Returns true if the pattern is contained in the set. False otherwise. 414 415 FIXME(zimbatm): this example doesn't work !!! 416 417 Example: 418 sys = mkSystem { } 419 matchAttrs { cpu = { bits = 64; }; } sys 420 => true 421 */ 422 matchAttrs = pattern: attrs: 423 fold or false (attrValues (zipAttrsWithNames (attrNames pattern) (n: values: 424 let pat = head values; val = head (tail values); in 425 if length values == 1 then false 426 else if isAttrs pat then isAttrs val && matchAttrs head values 427 else pat == val 428 ) [pattern attrs])); 429 430 /* Override only the attributes that are already present in the old set 431 useful for deep-overriding. 432 433 Example: 434 x = { a = { b = 4; c = 3; }; } 435 overrideExisting x { a = { b = 6; d = 2; }; } 436 => { a = { b = 6; d = 2; }; } 437 */ 438 overrideExisting = old: new: 439 old // listToAttrs (map (attr: nameValuePair attr (attrByPath [attr] old.${attr} new)) (attrNames old)); 440 441 /* Get a package output. 442 If no output is found, fallback to `.out` and then to the default. 443 444 Example: 445 getOutput "dev" pkgs.openssl 446 => "/nix/store/9rz8gxhzf8sw4kf2j2f1grr49w8zx5vj-openssl-1.0.1r-dev" 447 */ 448 getOutput = output: pkg: 449 if pkg.outputUnspecified or false 450 then pkg.${output} or pkg.out or pkg 451 else pkg; 452 453 getBin = getOutput "bin"; 454 getLib = getOutput "lib"; 455 getDev = getOutput "dev"; 456 457 /* Pick the outputs of packages to place in buildInputs */ 458 chooseDevOutputs = drvs: builtins.map (drv: if drv.outputUnspecified or false then drv.dev or drv else drv) drvs; 459 460 /*** deprecated stuff ***/ 461 462 zipWithNames = zipAttrsWithNames; 463 zip = builtins.trace 464 "lib.zip is deprecated, use lib.zipAttrsWith instead" zipAttrsWith; 465 466}