nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at python-updates 2059 lines 38 kB view raw
1/** 2 General list operations. 3*/ 4{ lib }: 5let 6 inherit (lib.strings) toInt; 7 inherit (lib.trivial) 8 compare 9 min 10 id 11 warn 12 ; 13 inherit (lib.attrsets) mapAttrs attrNames attrValues; 14 inherit (lib) max; 15in 16rec { 17 18 inherit (builtins) 19 head 20 tail 21 length 22 isList 23 elemAt 24 concatLists 25 filter 26 elem 27 genList 28 map 29 ; 30 31 /** 32 Create a list consisting of a single element. `singleton x` is 33 sometimes more convenient with respect to indentation than `[x]` 34 when x spans multiple lines. 35 36 # Inputs 37 38 `x` 39 40 : 1\. Function argument 41 42 # Type 43 44 ``` 45 singleton :: a -> [a] 46 ``` 47 48 # Examples 49 :::{.example} 50 ## `lib.lists.singleton` usage example 51 52 ```nix 53 singleton "foo" 54 => [ "foo" ] 55 ``` 56 57 ::: 58 */ 59 singleton = x: [ x ]; 60 61 /** 62 Apply the function to each element in the list. 63 Same as `map`, but arguments flipped. 64 65 # Inputs 66 67 `xs` 68 69 : 1\. Function argument 70 71 `f` 72 73 : 2\. Function argument 74 75 # Type 76 77 ``` 78 forEach :: [a] -> (a -> b) -> [b] 79 ``` 80 81 # Examples 82 :::{.example} 83 ## `lib.lists.forEach` usage example 84 85 ```nix 86 forEach [ 1 2 ] (x: 87 toString x 88 ) 89 => [ "1" "2" ] 90 ``` 91 92 ::: 93 */ 94 forEach = xs: f: map f xs; 95 96 /** 97 right fold a binary function `op` between successive elements of 98 `list` with `nul` as the starting value, i.e., 99 `foldr op nul [x_1 x_2 ... x_n] == op x_1 (op x_2 ... (op x_n nul))`. 100 101 # Inputs 102 103 `op` 104 105 : 1\. Function argument 106 107 `nul` 108 109 : 2\. Function argument 110 111 `list` 112 113 : 3\. Function argument 114 115 # Type 116 117 ``` 118 foldr :: (a -> b -> b) -> b -> [a] -> b 119 ``` 120 121 # Examples 122 :::{.example} 123 ## `lib.lists.foldr` usage example 124 125 ```nix 126 concat = foldr (a: b: a + b) "z" 127 concat [ "a" "b" "c" ] 128 => "abcz" 129 # different types 130 strange = foldr (int: str: toString (int + 1) + str) "a" 131 strange [ 1 2 3 4 ] 132 => "2345a" 133 ``` 134 135 ::: 136 */ 137 foldr = 138 op: nul: list: 139 let 140 len = length list; 141 fold' = n: if n == len then nul else op (elemAt list n) (fold' (n + 1)); 142 in 143 fold' 0; 144 145 /** 146 `fold` is an alias of `foldr` for historic reasons. 147 148 ::: {.warning} 149 This function will be removed in 26.05. 150 ::: 151 */ 152 fold = warn "fold has been deprecated, use foldr instead" foldr; 153 154 /** 155 left fold, like `foldr`, but from the left: 156 157 `foldl op nul [x_1 x_2 ... x_n] == op (... (op (op nul x_1) x_2) ... x_n)`. 158 159 # Inputs 160 161 `op` 162 163 : 1\. Function argument 164 165 `nul` 166 167 : 2\. Function argument 168 169 `list` 170 171 : 3\. Function argument 172 173 # Type 174 175 ``` 176 foldl :: (b -> a -> b) -> b -> [a] -> b 177 ``` 178 179 # Examples 180 :::{.example} 181 ## `lib.lists.foldl` usage example 182 183 ```nix 184 lconcat = foldl (a: b: a + b) "z" 185 lconcat [ "a" "b" "c" ] 186 => "zabc" 187 # different types 188 lstrange = foldl (str: int: str + toString (int + 1)) "a" 189 lstrange [ 1 2 3 4 ] 190 => "a2345" 191 ``` 192 193 ::: 194 */ 195 foldl = 196 op: nul: list: 197 let 198 foldl' = n: if n == -1 then nul else op (foldl' (n - 1)) (elemAt list n); 199 in 200 foldl' (length list - 1); 201 202 /** 203 Reduce a list by applying a binary operator from left to right, 204 starting with an initial accumulator. 205 206 Before each application of the operator, the accumulator value is evaluated. 207 This behavior makes this function stricter than [`foldl`](#function-library-lib.lists.foldl). 208 209 Unlike [`builtins.foldl'`](https://nixos.org/manual/nix/unstable/language/builtins.html#builtins-foldl'), 210 the initial accumulator argument is evaluated before the first iteration. 211 212 A call like 213 214 ```nix 215 foldl' op acc [ x x x ... x x ] 216 ``` 217 218 is (denotationally) equivalent to the following, 219 but with the added benefit that `foldl'` itself will never overflow the stack. 220 221 ```nix 222 let 223 acc = builtins.seq acc (op acc x ); 224 acc = builtins.seq acc (op acc x ); 225 acc = builtins.seq acc (op acc x ); 226 ... 227 acc = builtins.seq acc (op acc x); 228 acc = builtins.seq acc (op acc x ); 229 in 230 acc 231 232 # Or ignoring builtins.seq 233 op (op (... (op (op (op acc x) x) x) ...) x) x 234 ``` 235 236 # Inputs 237 238 `op` 239 240 : The binary operation to run, where the two arguments are: 241 242 1. `acc`: The current accumulator value: Either the initial one for the first iteration, or the result of the previous iteration 243 2. `x`: The corresponding list element for this iteration 244 245 `acc` 246 247 : The initial accumulator value. 248 249 The accumulator value is evaluated in any case before the first iteration starts. 250 251 To avoid evaluation even before the `list` argument is given an eta expansion can be used: 252 253 ```nix 254 list: lib.foldl' op acc list 255 ``` 256 257 `list` 258 259 : The list to fold 260 261 # Type 262 263 ``` 264 foldl' :: (acc -> x -> acc) -> acc -> [x] -> acc 265 ``` 266 267 # Examples 268 :::{.example} 269 ## `lib.lists.foldl'` usage example 270 271 ```nix 272 foldl' (acc: x: acc + x) 0 [1 2 3] 273 => 6 274 ``` 275 276 ::: 277 */ 278 foldl' = 279 op: acc: 280 # The builtin `foldl'` is a bit lazier than one might expect. 281 # See https://github.com/NixOS/nix/pull/7158. 282 # In particular, the initial accumulator value is not forced before the first iteration starts. 283 builtins.seq acc (builtins.foldl' op acc); 284 285 /** 286 Map with index starting from 0 287 288 # Inputs 289 290 `f` 291 292 : 1\. Function argument 293 294 `list` 295 296 : 2\. Function argument 297 298 # Type 299 300 ``` 301 imap0 :: (int -> a -> b) -> [a] -> [b] 302 ``` 303 304 # Examples 305 :::{.example} 306 ## `lib.lists.imap0` usage example 307 308 ```nix 309 imap0 (i: v: "${v}-${toString i}") ["a" "b"] 310 => [ "a-0" "b-1" ] 311 ``` 312 313 ::: 314 */ 315 imap0 = f: list: genList (n: f n (elemAt list n)) (length list); 316 317 /** 318 Map with index starting from 1 319 320 # Inputs 321 322 `f` 323 324 : 1\. Function argument 325 326 `list` 327 328 : 2\. Function argument 329 330 # Type 331 332 ``` 333 imap1 :: (int -> a -> b) -> [a] -> [b] 334 ``` 335 336 # Examples 337 :::{.example} 338 ## `lib.lists.imap1` usage example 339 340 ```nix 341 imap1 (i: v: "${v}-${toString i}") ["a" "b"] 342 => [ "a-1" "b-2" ] 343 ``` 344 345 ::: 346 */ 347 imap1 = f: list: genList (n: f (n + 1) (elemAt list n)) (length list); 348 349 /** 350 Filter a list for elements that satisfy a predicate function. 351 The predicate function is called with both the index and value for each element. 352 It must return `true`/`false` to include/exclude a given element in the result. 353 This function is strict in the result of the predicate function for each element. 354 This function has O(n) complexity. 355 356 Also see [`builtins.filter`](https://nixos.org/manual/nix/stable/language/builtins.html#builtins-filter) (available as `lib.lists.filter`), 357 which can be used instead when the index isn't needed. 358 359 # Inputs 360 361 `ipred` 362 363 : The predicate function, it takes two arguments: 364 - 1. (int): the index of the element. 365 - 2. (a): the value of the element. 366 367 It must return `true`/`false` to include/exclude a given element from the result. 368 369 `list` 370 371 : The list to filter using the predicate. 372 373 # Type 374 ``` 375 ifilter0 :: (int -> a -> bool) -> [a] -> [a] 376 ``` 377 378 # Examples 379 :::{.example} 380 ## `lib.lists.ifilter0` usage example 381 382 ```nix 383 ifilter0 (i: v: i == 0 || v > 2) [ 1 2 3 ] 384 => [ 1 3 ] 385 ``` 386 ::: 387 */ 388 ifilter0 = 389 ipred: input: 390 map (idx: elemAt input idx) ( 391 filter (idx: ipred idx (elemAt input idx)) (genList (x: x) (length input)) 392 ); 393 394 /** 395 Map and concatenate the result. 396 397 # Type 398 399 ``` 400 concatMap :: (a -> [b]) -> [a] -> [b] 401 ``` 402 403 # Examples 404 :::{.example} 405 ## `lib.lists.concatMap` usage example 406 407 ```nix 408 concatMap (x: [x] ++ ["z"]) ["a" "b"] 409 => [ "a" "z" "b" "z" ] 410 ``` 411 412 ::: 413 */ 414 concatMap = builtins.concatMap; 415 416 /** 417 Flatten the argument into a single list; that is, nested lists are 418 spliced into the top-level lists. 419 420 # Inputs 421 422 `x` 423 424 : 1\. Function argument 425 426 # Examples 427 :::{.example} 428 ## `lib.lists.flatten` usage example 429 430 ```nix 431 flatten [1 [2 [3] 4] 5] 432 => [1 2 3 4 5] 433 flatten 1 434 => [1] 435 ``` 436 437 ::: 438 */ 439 flatten = x: if isList x then concatMap (y: flatten y) x else [ x ]; 440 441 /** 442 Remove elements equal to `e` from a list. Useful for `buildInputs`. 443 444 # Inputs 445 446 `e` 447 448 : Element to remove from `list` 449 450 `list` 451 452 : The list 453 454 # Type 455 456 ``` 457 remove :: a -> [a] -> [a] 458 ``` 459 460 # Examples 461 :::{.example} 462 ## `lib.lists.remove` usage example 463 464 ```nix 465 remove 3 [ 1 3 4 3 ] 466 => [ 1 4 ] 467 ``` 468 469 ::: 470 */ 471 remove = e: filter (x: x != e); 472 473 /** 474 Find the sole element in the list matching the specified 475 predicate. 476 477 Returns `default` if no such element exists, or 478 `multiple` if there are multiple matching elements. 479 480 # Inputs 481 482 `pred` 483 484 : Predicate 485 486 `default` 487 488 : Default value to return if element was not found. 489 490 `multiple` 491 492 : Default value to return if more than one element was found 493 494 `list` 495 496 : Input list 497 498 # Type 499 500 ``` 501 findSingle :: (a -> bool) -> a -> a -> [a] -> a 502 ``` 503 504 # Examples 505 :::{.example} 506 ## `lib.lists.findSingle` usage example 507 508 ```nix 509 findSingle (x: x == 3) "none" "multiple" [ 1 3 3 ] 510 => "multiple" 511 findSingle (x: x == 3) "none" "multiple" [ 1 3 ] 512 => 3 513 findSingle (x: x == 3) "none" "multiple" [ 1 9 ] 514 => "none" 515 ``` 516 517 ::: 518 */ 519 findSingle = 520 pred: default: multiple: list: 521 let 522 found = filter pred list; 523 len = length found; 524 in 525 if len == 0 then 526 default 527 else if len != 1 then 528 multiple 529 else 530 head found; 531 532 /** 533 Find the first index in the list matching the specified 534 predicate or return `default` if no such element exists. 535 536 # Inputs 537 538 `pred` 539 540 : Predicate 541 542 `default` 543 544 : Default value to return 545 546 `list` 547 548 : Input list 549 550 # Type 551 552 ``` 553 findFirstIndex :: (a -> Bool) -> b -> [a] -> (Int | b) 554 ``` 555 556 # Examples 557 :::{.example} 558 ## `lib.lists.findFirstIndex` usage example 559 560 ```nix 561 findFirstIndex (x: x > 3) null [ 0 6 4 ] 562 => 1 563 findFirstIndex (x: x > 9) null [ 0 6 4 ] 564 => null 565 ``` 566 567 ::: 568 */ 569 findFirstIndex = 570 pred: default: list: 571 let 572 # A naive recursive implementation would be much simpler, but 573 # would also overflow the evaluator stack. We use `foldl'` as a workaround 574 # because it reuses the same stack space, evaluating the function for one 575 # element after another. We can't return early, so this means that we 576 # sacrifice early cutoff, but that appears to be an acceptable cost. A 577 # clever scheme with "exponential search" is possible, but appears over- 578 # engineered for now. See https://github.com/NixOS/nixpkgs/pull/235267 579 580 # Invariant: 581 # - if index < 0 then el == elemAt list (- index - 1) and all elements before el didn't satisfy pred 582 # - if index >= 0 then pred (elemAt list index) and all elements before (elemAt list index) didn't satisfy pred 583 # 584 # We start with index -1 and the 0'th element of the list, which satisfies the invariant 585 resultIndex = foldl' ( 586 index: el: 587 if index < 0 then 588 # No match yet before the current index, we need to check the element 589 if pred el then 590 # We have a match! Turn it into the actual index to prevent future iterations from modifying it 591 -index - 1 592 else 593 # Still no match, update the index to the next element (we're counting down, so minus one) 594 index - 1 595 else 596 # There's already a match, propagate the index without evaluating anything 597 index 598 ) (-1) list; 599 in 600 if resultIndex < 0 then default else resultIndex; 601 602 /** 603 Find the first element in the list matching the specified 604 predicate or return `default` if no such element exists. 605 606 # Inputs 607 608 `pred` 609 610 : Predicate 611 612 `default` 613 614 : Default value to return 615 616 `list` 617 618 : Input list 619 620 # Type 621 622 ``` 623 findFirst :: (a -> bool) -> a -> [a] -> a 624 ``` 625 626 # Examples 627 :::{.example} 628 ## `lib.lists.findFirst` usage example 629 630 ```nix 631 findFirst (x: x > 3) 7 [ 1 6 4 ] 632 => 6 633 findFirst (x: x > 9) 7 [ 1 6 4 ] 634 => 7 635 ``` 636 637 ::: 638 */ 639 findFirst = 640 pred: default: list: 641 let 642 index = findFirstIndex pred null list; 643 in 644 if index == null then default else elemAt list index; 645 646 /** 647 Returns true if function `pred` returns true for at least one 648 element of `list`. 649 650 # Inputs 651 652 `pred` 653 654 : Predicate 655 656 `list` 657 658 : Input list 659 660 # Type 661 662 ``` 663 any :: (a -> bool) -> [a] -> bool 664 ``` 665 666 # Examples 667 :::{.example} 668 ## `lib.lists.any` usage example 669 670 ```nix 671 any isString [ 1 "a" { } ] 672 => true 673 any isString [ 1 { } ] 674 => false 675 ``` 676 677 ::: 678 */ 679 any = builtins.any; 680 681 /** 682 Returns true if function `pred` returns true for all elements of 683 `list`. 684 685 # Inputs 686 687 `pred` 688 689 : Predicate 690 691 `list` 692 693 : Input list 694 695 # Type 696 697 ``` 698 all :: (a -> bool) -> [a] -> bool 699 ``` 700 701 # Examples 702 :::{.example} 703 ## `lib.lists.all` usage example 704 705 ```nix 706 all (x: x < 3) [ 1 2 ] 707 => true 708 all (x: x < 3) [ 1 2 3 ] 709 => false 710 ``` 711 712 ::: 713 */ 714 all = builtins.all; 715 716 /** 717 Count how many elements of `list` match the supplied predicate 718 function. 719 720 # Inputs 721 722 `pred` 723 724 : Predicate 725 726 # Type 727 728 ``` 729 count :: (a -> bool) -> [a] -> int 730 ``` 731 732 # Examples 733 :::{.example} 734 ## `lib.lists.count` usage example 735 736 ```nix 737 count (x: x == 3) [ 3 2 3 4 6 ] 738 => 2 739 ``` 740 741 ::: 742 */ 743 count = pred: foldl' (c: x: if pred x then c + 1 else c) 0; 744 745 /** 746 Return a singleton list or an empty list, depending on a boolean 747 value. Useful when building lists with optional elements 748 (e.g. `++ optional (system == "i686-linux") firefox`). 749 750 # Inputs 751 752 `cond` 753 754 : 1\. Function argument 755 756 `elem` 757 758 : 2\. Function argument 759 760 # Type 761 762 ``` 763 optional :: bool -> a -> [a] 764 ``` 765 766 # Examples 767 :::{.example} 768 ## `lib.lists.optional` usage example 769 770 ```nix 771 optional true "foo" 772 => [ "foo" ] 773 optional false "foo" 774 => [ ] 775 ``` 776 777 ::: 778 */ 779 optional = cond: elem: if cond then [ elem ] else [ ]; 780 781 /** 782 Returns a list or an empty list, depending on a boolean value. 783 784 # Inputs 785 786 `cond` 787 788 : Condition 789 790 `elems` 791 792 : List to return if condition is true 793 794 # Type 795 796 ``` 797 optionals :: bool -> [a] -> [a] 798 ``` 799 800 # Examples 801 :::{.example} 802 ## `lib.lists.optionals` usage example 803 804 ```nix 805 optionals true [ 2 3 ] 806 => [ 2 3 ] 807 optionals false [ 2 3 ] 808 => [ ] 809 ``` 810 811 ::: 812 */ 813 optionals = cond: elems: if cond then elems else [ ]; 814 815 /** 816 If argument is a list, return it; else, wrap it in a singleton 817 list. If you're using this, you should almost certainly 818 reconsider if there isn't a more "well-typed" approach. 819 820 # Inputs 821 822 `x` 823 824 : 1\. Function argument 825 826 # Examples 827 :::{.example} 828 ## `lib.lists.toList` usage example 829 830 ```nix 831 toList [ 1 2 ] 832 => [ 1 2 ] 833 toList "hi" 834 => [ "hi" ] 835 ``` 836 837 ::: 838 */ 839 toList = x: if isList x then x else [ x ]; 840 841 /** 842 Returns a list of integers from `first` up to and including `last`. 843 844 # Inputs 845 846 `first` 847 848 : First integer in the range 849 850 `last` 851 852 : Last integer in the range 853 854 # Type 855 856 ``` 857 range :: int -> int -> [int] 858 ``` 859 860 # Examples 861 :::{.example} 862 ## `lib.lists.range` usage example 863 864 ```nix 865 range 2 4 866 => [ 2 3 4 ] 867 range 3 2 868 => [ ] 869 ``` 870 871 ::: 872 */ 873 range = first: last: if first > last then [ ] else genList (n: first + n) (last - first + 1); 874 875 /** 876 Returns a list with `n` copies of an element. 877 878 # Inputs 879 880 `n` 881 882 : 1\. Function argument 883 884 `elem` 885 886 : 2\. Function argument 887 888 # Type 889 890 ``` 891 replicate :: int -> a -> [a] 892 ``` 893 894 # Examples 895 :::{.example} 896 ## `lib.lists.replicate` usage example 897 898 ```nix 899 replicate 3 "a" 900 => [ "a" "a" "a" ] 901 replicate 2 true 902 => [ true true ] 903 ``` 904 905 ::: 906 */ 907 replicate = n: elem: genList (_: elem) n; 908 909 /** 910 Splits the elements of a list in two lists, `right` and 911 `wrong`, depending on the evaluation of a predicate. 912 913 # Inputs 914 915 `pred` 916 917 : Predicate 918 919 `list` 920 921 : Input list 922 923 # Type 924 925 ``` 926 (a -> bool) -> [a] -> { right :: [a]; wrong :: [a]; } 927 ``` 928 929 # Examples 930 :::{.example} 931 ## `lib.lists.partition` usage example 932 933 ```nix 934 partition (x: x > 2) [ 5 1 2 3 4 ] 935 => { right = [ 5 3 4 ]; wrong = [ 1 2 ]; } 936 ``` 937 938 ::: 939 */ 940 partition = builtins.partition; 941 942 /** 943 Splits the elements of a list into many lists, using the return value of a predicate. 944 Predicate should return a string which becomes keys of attrset `groupBy` returns. 945 `groupBy'` allows to customise the combining function and initial value 946 947 # Inputs 948 949 `op` 950 951 : 1\. Function argument 952 953 `nul` 954 955 : 2\. Function argument 956 957 `pred` 958 959 : 3\. Function argument 960 961 `lst` 962 963 : 4\. Function argument 964 965 # Examples 966 :::{.example} 967 ## `lib.lists.groupBy'` usage example 968 969 ```nix 970 groupBy (x: boolToString (x > 2)) [ 5 1 2 3 4 ] 971 => { true = [ 5 3 4 ]; false = [ 1 2 ]; } 972 groupBy (x: x.name) [ {name = "icewm"; script = "icewm &";} 973 {name = "xfce"; script = "xfce4-session &";} 974 {name = "icewm"; script = "icewmbg &";} 975 {name = "mate"; script = "gnome-session &";} 976 ] 977 => { icewm = [ { name = "icewm"; script = "icewm &"; } 978 { name = "icewm"; script = "icewmbg &"; } ]; 979 mate = [ { name = "mate"; script = "gnome-session &"; } ]; 980 xfce = [ { name = "xfce"; script = "xfce4-session &"; } ]; 981 } 982 983 groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ] 984 => { true = 12; false = 3; } 985 ``` 986 987 ::: 988 */ 989 groupBy' = 990 op: nul: pred: lst: 991 mapAttrs (name: foldl op nul) (groupBy pred lst); 992 993 groupBy = 994 builtins.groupBy or ( 995 pred: 996 foldl' ( 997 r: e: 998 let 999 key = pred e; 1000 in 1001 r // { ${key} = (r.${key} or [ ]) ++ [ e ]; } 1002 ) { } 1003 ); 1004 1005 /** 1006 Merges two lists of the same size together. If the sizes aren't the same 1007 the merging stops at the shortest. How both lists are merged is defined 1008 by the first argument. 1009 1010 # Inputs 1011 1012 `f` 1013 1014 : Function to zip elements of both lists 1015 1016 `fst` 1017 1018 : First list 1019 1020 `snd` 1021 1022 : Second list 1023 1024 # Type 1025 1026 ``` 1027 zipListsWith :: (a -> b -> c) -> [a] -> [b] -> [c] 1028 ``` 1029 1030 # Examples 1031 :::{.example} 1032 ## `lib.lists.zipListsWith` usage example 1033 1034 ```nix 1035 zipListsWith (a: b: a + b) ["h" "l"] ["e" "o"] 1036 => ["he" "lo"] 1037 ``` 1038 1039 ::: 1040 */ 1041 zipListsWith = 1042 f: fst: snd: 1043 genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd)); 1044 1045 /** 1046 Merges two lists of the same size together. If the sizes aren't the same 1047 the merging stops at the shortest. 1048 1049 # Inputs 1050 1051 `fst` 1052 1053 : First list 1054 1055 `snd` 1056 1057 : Second list 1058 1059 # Type 1060 1061 ``` 1062 zipLists :: [a] -> [b] -> [{ fst :: a; snd :: b; }] 1063 ``` 1064 1065 # Examples 1066 :::{.example} 1067 ## `lib.lists.zipLists` usage example 1068 1069 ```nix 1070 zipLists [ 1 2 ] [ "a" "b" ] 1071 => [ { fst = 1; snd = "a"; } { fst = 2; snd = "b"; } ] 1072 ``` 1073 1074 ::: 1075 */ 1076 zipLists = zipListsWith (fst: snd: { inherit fst snd; }); 1077 1078 /** 1079 Reverse the order of the elements of a list. 1080 1081 # Inputs 1082 1083 `xs` 1084 1085 : 1\. Function argument 1086 1087 # Type 1088 1089 ``` 1090 reverseList :: [a] -> [a] 1091 ``` 1092 1093 # Examples 1094 :::{.example} 1095 ## `lib.lists.reverseList` usage example 1096 1097 ```nix 1098 reverseList [ "b" "o" "j" ] 1099 => [ "j" "o" "b" ] 1100 ``` 1101 1102 ::: 1103 */ 1104 reverseList = 1105 xs: 1106 let 1107 l = length xs; 1108 in 1109 genList (n: elemAt xs (l - n - 1)) l; 1110 1111 /** 1112 Depth-First Search (DFS) for lists `list != []`. 1113 1114 `before a b == true` means that `b` depends on `a` (there's an 1115 edge from `b` to `a`). 1116 1117 # Inputs 1118 1119 `stopOnCycles` 1120 1121 : 1\. Function argument 1122 1123 `before` 1124 1125 : 2\. Function argument 1126 1127 `list` 1128 1129 : 3\. Function argument 1130 1131 # Examples 1132 :::{.example} 1133 ## `lib.lists.listDfs` usage example 1134 1135 ```nix 1136 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" ] 1137 == { minimal = "/"; # minimal element 1138 visited = [ "/home/user" ]; # seen elements (in reverse order) 1139 rest = [ "/home" "other" ]; # everything else 1140 } 1141 1142 listDfs true hasPrefix [ "/home/user" "other" "/" "/home" "/" ] 1143 == { cycle = "/"; # cycle encountered at this element 1144 loops = [ "/" ]; # and continues to these elements 1145 visited = [ "/" "/home/user" ]; # elements leading to the cycle (in reverse order) 1146 rest = [ "/home" "other" ]; # everything else 1147 ``` 1148 1149 ::: 1150 */ 1151 listDfs = 1152 stopOnCycles: before: list: 1153 let 1154 dfs' = 1155 us: visited: rest: 1156 let 1157 c = filter (x: before x us) visited; 1158 b = partition (x: before x us) rest; 1159 in 1160 if stopOnCycles && (length c > 0) then 1161 { 1162 cycle = us; 1163 loops = c; 1164 inherit visited rest; 1165 } 1166 else if length b.right == 0 then 1167 # nothing is before us 1168 { 1169 minimal = us; 1170 inherit visited rest; 1171 } 1172 else 1173 # grab the first one before us and continue 1174 dfs' (head b.right) ([ us ] ++ visited) (tail b.right ++ b.wrong); 1175 in 1176 dfs' (head list) [ ] (tail list); 1177 1178 /** 1179 Sort a list based on a partial ordering using DFS. This 1180 implementation is O(N^2), if your ordering is linear, use `sort` 1181 instead. 1182 1183 `before a b == true` means that `b` should be after `a` 1184 in the result. 1185 1186 # Inputs 1187 1188 `before` 1189 1190 : 1\. Function argument 1191 1192 `list` 1193 1194 : 2\. Function argument 1195 1196 # Examples 1197 :::{.example} 1198 ## `lib.lists.toposort` usage example 1199 1200 ```nix 1201 toposort hasPrefix [ "/home/user" "other" "/" "/home" ] 1202 == { result = [ "/" "/home" "/home/user" "other" ]; } 1203 1204 toposort hasPrefix [ "/home/user" "other" "/" "/home" "/" ] 1205 == { cycle = [ "/home/user" "/" "/" ]; # path leading to a cycle 1206 loops = [ "/" ]; } # loops back to these elements 1207 1208 toposort hasPrefix [ "other" "/home/user" "/home" "/" ] 1209 == { result = [ "other" "/" "/home" "/home/user" ]; } 1210 1211 toposort (a: b: a < b) [ 3 2 1 ] == { result = [ 1 2 3 ]; } 1212 ``` 1213 1214 ::: 1215 */ 1216 toposort = 1217 before: list: 1218 let 1219 dfsthis = listDfs true before list; 1220 toporest = toposort before (dfsthis.visited ++ dfsthis.rest); 1221 in 1222 if length list < 2 then 1223 # finish 1224 { result = list; } 1225 else if dfsthis ? cycle then 1226 # there's a cycle, starting from the current vertex, return it 1227 { 1228 cycle = reverseList ([ dfsthis.cycle ] ++ dfsthis.visited); 1229 inherit (dfsthis) loops; 1230 } 1231 else if toporest ? cycle then 1232 # there's a cycle somewhere else in the graph, return it 1233 toporest 1234 # Slow, but short. Can be made a bit faster with an explicit stack. 1235 else 1236 # there are no cycles 1237 { result = [ dfsthis.minimal ] ++ toporest.result; }; 1238 1239 /** 1240 Sort a list based on a comparator function which compares two 1241 elements and returns true if the first argument is strictly below 1242 the second argument. The returned list is sorted in an increasing 1243 order. The implementation does a quick-sort. 1244 1245 See also [`sortOn`](#function-library-lib.lists.sortOn), which applies the 1246 default comparison on a function-derived property, and may be more efficient. 1247 1248 # Inputs 1249 1250 `comparator` 1251 1252 : 1\. Function argument 1253 1254 `list` 1255 1256 : 2\. Function argument 1257 1258 # Type 1259 1260 ``` 1261 sort :: (a -> a -> Bool) -> [a] -> [a] 1262 ``` 1263 1264 # Examples 1265 :::{.example} 1266 ## `lib.lists.sort` usage example 1267 1268 ```nix 1269 sort (p: q: p < q) [ 5 3 7 ] 1270 => [ 3 5 7 ] 1271 ``` 1272 1273 ::: 1274 */ 1275 sort = builtins.sort; 1276 1277 /** 1278 Sort a list based on the default comparison of a derived property `b`. 1279 1280 The items are returned in `b`-increasing order. 1281 1282 **Performance**: 1283 1284 The passed function `f` is only evaluated once per item, 1285 unlike an unprepared [`sort`](#function-library-lib.lists.sort) using 1286 `f p < f q`. 1287 1288 **Laws**: 1289 ```nix 1290 sortOn f == sort (p: q: f p < f q) 1291 ``` 1292 1293 # Inputs 1294 1295 `f` 1296 1297 : 1\. Function argument 1298 1299 `list` 1300 1301 : 2\. Function argument 1302 1303 # Type 1304 1305 ``` 1306 sortOn :: (a -> b) -> [a] -> [a], for comparable b 1307 ``` 1308 1309 # Examples 1310 :::{.example} 1311 ## `lib.lists.sortOn` usage example 1312 1313 ```nix 1314 sortOn stringLength [ "aa" "b" "cccc" ] 1315 => [ "b" "aa" "cccc" ] 1316 ``` 1317 1318 ::: 1319 */ 1320 sortOn = 1321 f: list: 1322 let 1323 # Heterogenous list as pair may be ugly, but requires minimal allocations. 1324 pairs = map (x: [ 1325 (f x) 1326 x 1327 ]) list; 1328 in 1329 map (x: builtins.elemAt x 1) ( 1330 sort 1331 # Compare the first element of the pairs 1332 # Do not factor out the `<`, to avoid calls in hot code; duplicate instead. 1333 (a: b: head a < head b) 1334 pairs 1335 ); 1336 1337 /** 1338 Compare two lists element-by-element with a comparison function `cmp`. 1339 1340 List elements are compared pairwise in order by the provided comparison function `cmp`, 1341 the first non-equal pair of elements determines the result. 1342 1343 :::{.note} 1344 The `<` operator can also be used to compare lists using a boolean condition. (e.g. `[1 2] < [1 3]` is `true`). 1345 See also [language operators](https://nix.dev/manual/nix/stable/language/operators#comparison) for more information. 1346 ::: 1347 1348 # Inputs 1349 1350 `cmp` 1351 1352 : The comparison function `a: b: ...` must return: 1353 - `0` if `a` and `b` are equal 1354 - `1` if `a` is greater than `b` 1355 - `-1` if `a` is less than `b` 1356 1357 See [lib.compare](#function-library-lib.trivial.compare) for a an example implementation. 1358 1359 `a` 1360 1361 : The first list 1362 1363 `b` 1364 1365 : The second list 1366 1367 # Examples 1368 :::{.example} 1369 ## `lib.lists.compareLists` usage examples 1370 1371 ```nix 1372 compareLists lib.compare [] [] 1373 => 0 1374 compareLists lib.compare [] [ "a" ] 1375 => -1 1376 compareLists lib.compare [ "a" ] [] 1377 => 1 1378 compareLists lib.compare [ "a" "b" ] [ "a" "c" ] 1379 => -1 1380 ``` 1381 1382 ::: 1383 */ 1384 compareLists = 1385 cmp: a: b: 1386 if a == [ ] then 1387 if b == [ ] then 0 else -1 1388 else if b == [ ] then 1389 1 1390 else 1391 let 1392 rel = cmp (head a) (head b); 1393 in 1394 if rel == 0 then compareLists cmp (tail a) (tail b) else rel; 1395 1396 /** 1397 Sort list using "Natural sorting". 1398 Numeric portions of strings are sorted in numeric order. 1399 1400 # Inputs 1401 1402 `lst` 1403 1404 : 1\. Function argument 1405 1406 # Examples 1407 :::{.example} 1408 ## `lib.lists.naturalSort` usage example 1409 1410 ```nix 1411 naturalSort ["disk11" "disk8" "disk100" "disk9"] 1412 => ["disk8" "disk9" "disk11" "disk100"] 1413 naturalSort ["10.46.133.149" "10.5.16.62" "10.54.16.25"] 1414 => ["10.5.16.62" "10.46.133.149" "10.54.16.25"] 1415 naturalSort ["v0.2" "v0.15" "v0.0.9"] 1416 => [ "v0.0.9" "v0.2" "v0.15" ] 1417 ``` 1418 1419 ::: 1420 */ 1421 naturalSort = 1422 lst: 1423 let 1424 vectorise = s: map (x: if isList x then toInt (head x) else x) (builtins.split "(0|[1-9][0-9]*)" s); 1425 prepared = map (x: [ 1426 (vectorise x) 1427 x 1428 ]) lst; # remember vectorised version for O(n) regex splits 1429 less = a: b: (compareLists compare (head a) (head b)) < 0; 1430 in 1431 map (x: elemAt x 1) (sort less prepared); 1432 1433 /** 1434 Returns the first (at most) N elements of a list. 1435 1436 # Inputs 1437 1438 `count` 1439 1440 : Number of elements to take 1441 1442 `list` 1443 1444 : Input list 1445 1446 # Type 1447 1448 ``` 1449 take :: int -> [a] -> [a] 1450 ``` 1451 1452 # Examples 1453 :::{.example} 1454 ## `lib.lists.take` usage example 1455 1456 ```nix 1457 take 2 [ "a" "b" "c" "d" ] 1458 => [ "a" "b" ] 1459 take 2 [ ] 1460 => [ ] 1461 ``` 1462 1463 ::: 1464 */ 1465 take = count: sublist 0 count; 1466 1467 /** 1468 Returns the last (at most) N elements of a list. 1469 1470 # Inputs 1471 1472 `count` 1473 1474 : Maximum number of elements to pick 1475 1476 `list` 1477 1478 : Input list 1479 1480 # Type 1481 1482 ``` 1483 takeEnd :: int -> [a] -> [a] 1484 ``` 1485 1486 # Examples 1487 :::{.example} 1488 ## `lib.lists.takeEnd` usage example 1489 1490 ```nix 1491 takeEnd 2 [ "a" "b" "c" "d" ] 1492 => [ "c" "d" ] 1493 takeEnd 2 [ ] 1494 => [ ] 1495 ``` 1496 1497 ::: 1498 */ 1499 takeEnd = n: xs: drop (max 0 (length xs - n)) xs; 1500 1501 /** 1502 Remove the first (at most) N elements of a list. 1503 1504 # Inputs 1505 1506 `count` 1507 1508 : Number of elements to drop 1509 1510 `list` 1511 1512 : Input list 1513 1514 # Type 1515 1516 ``` 1517 drop :: int -> [a] -> [a] 1518 ``` 1519 1520 # Examples 1521 :::{.example} 1522 ## `lib.lists.drop` usage example 1523 1524 ```nix 1525 drop 2 [ "a" "b" "c" "d" ] 1526 => [ "c" "d" ] 1527 drop 2 [ ] 1528 => [ ] 1529 ``` 1530 1531 ::: 1532 */ 1533 drop = count: list: sublist count (length list) list; 1534 1535 /** 1536 Remove the last (at most) N elements of a list. 1537 1538 # Inputs 1539 1540 `count` 1541 1542 : Number of elements to drop 1543 1544 `list` 1545 1546 : Input list 1547 1548 # Type 1549 1550 ``` 1551 dropEnd :: Int -> [a] -> [a] 1552 ``` 1553 1554 # Examples 1555 1556 :::{.example} 1557 ## `lib.lists.dropEnd` usage example 1558 1559 ```nix 1560 dropEnd 2 [ "a" "b" "c" "d" ] 1561 => [ "a" "b" ] 1562 dropEnd 2 [ ] 1563 => [ ] 1564 ``` 1565 ::: 1566 */ 1567 dropEnd = n: xs: take (max 0 (length xs - n)) xs; 1568 1569 /** 1570 Whether the first list is a prefix of the second list. 1571 1572 # Inputs 1573 1574 `list1` 1575 1576 : 1\. Function argument 1577 1578 `list2` 1579 1580 : 2\. Function argument 1581 1582 # Type 1583 1584 ``` 1585 hasPrefix :: [a] -> [a] -> bool 1586 ``` 1587 1588 # Examples 1589 :::{.example} 1590 ## `lib.lists.hasPrefix` usage example 1591 1592 ```nix 1593 hasPrefix [ 1 2 ] [ 1 2 3 4 ] 1594 => true 1595 hasPrefix [ 0 1 ] [ 1 2 3 4 ] 1596 => false 1597 ``` 1598 1599 ::: 1600 */ 1601 hasPrefix = list1: list2: take (length list1) list2 == list1; 1602 1603 /** 1604 Remove the first list as a prefix from the second list. 1605 Error if the first list isn't a prefix of the second list. 1606 1607 # Inputs 1608 1609 `list1` 1610 1611 : 1\. Function argument 1612 1613 `list2` 1614 1615 : 2\. Function argument 1616 1617 # Type 1618 1619 ``` 1620 removePrefix :: [a] -> [a] -> [a] 1621 ``` 1622 1623 # Examples 1624 :::{.example} 1625 ## `lib.lists.removePrefix` usage example 1626 1627 ```nix 1628 removePrefix [ 1 2 ] [ 1 2 3 4 ] 1629 => [ 3 4 ] 1630 removePrefix [ 0 1 ] [ 1 2 3 4 ] 1631 => <error> 1632 ``` 1633 1634 ::: 1635 */ 1636 removePrefix = 1637 list1: list2: 1638 if hasPrefix list1 list2 then 1639 drop (length list1) list2 1640 else 1641 throw "lib.lists.removePrefix: First argument is not a list prefix of the second argument"; 1642 1643 /** 1644 Returns a list consisting of at most `count` elements of `list`, 1645 starting at index `start`. 1646 1647 # Inputs 1648 1649 `start` 1650 1651 : Index at which to start the sublist 1652 1653 `count` 1654 1655 : Number of elements to take 1656 1657 `list` 1658 1659 : Input list 1660 1661 # Type 1662 1663 ``` 1664 sublist :: int -> int -> [a] -> [a] 1665 ``` 1666 1667 # Examples 1668 :::{.example} 1669 ## `lib.lists.sublist` usage example 1670 1671 ```nix 1672 sublist 1 3 [ "a" "b" "c" "d" "e" ] 1673 => [ "b" "c" "d" ] 1674 sublist 1 3 [ ] 1675 => [ ] 1676 ``` 1677 1678 ::: 1679 */ 1680 sublist = 1681 start: count: list: 1682 let 1683 len = length list; 1684 in 1685 genList (n: elemAt list (n + start)) ( 1686 if start >= len then 1687 0 1688 else if start + count > len then 1689 len - start 1690 else 1691 count 1692 ); 1693 1694 /** 1695 The common prefix of two lists. 1696 1697 # Inputs 1698 1699 `list1` 1700 1701 : 1\. Function argument 1702 1703 `list2` 1704 1705 : 2\. Function argument 1706 1707 # Type 1708 1709 ``` 1710 commonPrefix :: [a] -> [a] -> [a] 1711 ``` 1712 1713 # Examples 1714 :::{.example} 1715 ## `lib.lists.commonPrefix` usage example 1716 1717 ```nix 1718 commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ] 1719 => [ 1 2 ] 1720 commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ] 1721 => [ 1 2 3 ] 1722 commonPrefix [ 1 2 3 ] [ 4 5 6 ] 1723 => [ ] 1724 ``` 1725 1726 ::: 1727 */ 1728 commonPrefix = 1729 list1: list2: 1730 let 1731 # Zip the lists together into a list of booleans whether each element matches 1732 matchings = zipListsWith (fst: snd: fst != snd) list1 list2; 1733 # Find the first index where the elements don't match, 1734 # which will then also be the length of the common prefix. 1735 # If all elements match, we fall back to the length of the zipped list, 1736 # which is the same as the length of the smaller list. 1737 commonPrefixLength = findFirstIndex id (length matchings) matchings; 1738 in 1739 take commonPrefixLength list1; 1740 1741 /** 1742 Returns the last element of a list. 1743 1744 This function throws an error if the list is empty. 1745 1746 # Inputs 1747 1748 `list` 1749 1750 : 1\. Function argument 1751 1752 # Type 1753 1754 ``` 1755 last :: [a] -> a 1756 ``` 1757 1758 # Examples 1759 :::{.example} 1760 ## `lib.lists.last` usage example 1761 1762 ```nix 1763 last [ 1 2 3 ] 1764 => 3 1765 ``` 1766 1767 ::: 1768 */ 1769 last = 1770 list: 1771 assert lib.assertMsg (list != [ ]) "lists.last: list must not be empty!"; 1772 elemAt list (length list - 1); 1773 1774 /** 1775 Returns all elements but the last. 1776 1777 This function throws an error if the list is empty. 1778 1779 # Inputs 1780 1781 `list` 1782 1783 : 1\. Function argument 1784 1785 # Type 1786 1787 ``` 1788 init :: [a] -> [a] 1789 ``` 1790 1791 # Examples 1792 :::{.example} 1793 ## `lib.lists.init` usage example 1794 1795 ```nix 1796 init [ 1 2 3 ] 1797 => [ 1 2 ] 1798 ``` 1799 1800 ::: 1801 */ 1802 init = 1803 list: 1804 assert lib.assertMsg (list != [ ]) "lists.init: list must not be empty!"; 1805 take (length list - 1) list; 1806 1807 /** 1808 Returns the image of the cross product of some lists by a function. 1809 1810 # Examples 1811 :::{.example} 1812 ## `lib.lists.crossLists` usage example 1813 1814 ```nix 1815 crossLists (x: y: "${toString x}${toString y}") [[1 2] [3 4]] 1816 => [ "13" "14" "23" "24" ] 1817 ``` 1818 1819 If you have an attrset already, consider mapCartesianProduct: 1820 1821 ```nix 1822 mapCartesianProduct (x: "${toString x.a}${toString x.b}") { a = [1 2]; b = [3 4]; } 1823 => [ "13" "14" "23" "24" ] 1824 ``` 1825 ::: 1826 */ 1827 crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [ f ]; 1828 1829 /** 1830 Remove duplicate elements from the `list`. O(n^2) complexity. 1831 1832 :::{.note} 1833 If the list only contains strings and order is not important, the complexity can be reduced to O(n log n) by using [`lib.lists.uniqueStrings`](#function-library-lib.lists.uniqueStrings) instead. 1834 ::: 1835 1836 # Inputs 1837 1838 `list` 1839 1840 : Input list 1841 1842 # Type 1843 1844 ``` 1845 unique :: [a] -> [a] 1846 ``` 1847 1848 # Examples 1849 :::{.example} 1850 ## `lib.lists.unique` usage example 1851 1852 ```nix 1853 unique [ 3 2 3 4 ] 1854 => [ 3 2 4 ] 1855 ``` 1856 1857 ::: 1858 */ 1859 unique = foldl' (acc: e: if elem e acc then acc else acc ++ [ e ]) [ ]; 1860 1861 /** 1862 Removes duplicate strings from the `list`. O(n log n) complexity. 1863 1864 :::{.note} 1865 Order is not preserved. 1866 1867 All elements of the list must be strings without context. 1868 1869 This function fails when the list contains a non-string element or a [string with context](https://nix.dev/manual/nix/latest/language/string-context.html). 1870 In that case use [`lib.lists.unique`](#function-library-lib.lists.unique) instead. 1871 ::: 1872 1873 # Inputs 1874 1875 `list` 1876 1877 : List of strings 1878 1879 # Type 1880 1881 ``` 1882 uniqueStrings :: [ String ] -> [ String ] 1883 ``` 1884 1885 # Examples 1886 :::{.example} 1887 ## `lib.lists.uniqueStrings` usage example 1888 1889 ```nix 1890 uniqueStrings [ "foo" "bar" "foo" ] 1891 => [ "bar" "foo" ] # order is not preserved 1892 ``` 1893 1894 ::: 1895 */ 1896 uniqueStrings = list: attrNames (groupBy id list); 1897 1898 /** 1899 Check if list contains only unique elements. O(n^2) complexity. 1900 1901 # Inputs 1902 1903 `list` 1904 1905 : 1\. Function argument 1906 1907 # Type 1908 1909 ``` 1910 allUnique :: [a] -> bool 1911 ``` 1912 1913 # Examples 1914 :::{.example} 1915 ## `lib.lists.allUnique` usage example 1916 1917 ```nix 1918 allUnique [ 3 2 3 4 ] 1919 => false 1920 allUnique [ 3 2 4 1 ] 1921 => true 1922 ``` 1923 1924 ::: 1925 */ 1926 allUnique = list: (length (unique list) == length list); 1927 1928 /** 1929 Intersects list `list1` and another list (`list2`). 1930 1931 O(nm) complexity. 1932 1933 # Inputs 1934 1935 `list1` 1936 1937 : First list 1938 1939 `list2` 1940 1941 : Second list 1942 1943 # Examples 1944 :::{.example} 1945 ## `lib.lists.intersectLists` usage example 1946 1947 ```nix 1948 intersectLists [ 1 2 3 ] [ 6 3 2 ] 1949 => [ 3 2 ] 1950 ``` 1951 1952 ::: 1953 */ 1954 intersectLists = e: filter (x: elem x e); 1955 1956 /** 1957 Subtracts list `e` from another list (`list2`). 1958 1959 O(nm) complexity. 1960 1961 # Inputs 1962 1963 `e` 1964 1965 : First list 1966 1967 `list2` 1968 1969 : Second list 1970 1971 # Examples 1972 :::{.example} 1973 ## `lib.lists.subtractLists` usage example 1974 1975 ```nix 1976 subtractLists [ 3 2 ] [ 1 2 3 4 5 3 ] 1977 => [ 1 4 5 ] 1978 ``` 1979 1980 ::: 1981 */ 1982 subtractLists = e: filter (x: !(elem x e)); 1983 1984 /** 1985 Test if two lists have no common element. 1986 It should be slightly more efficient than `intersectLists a b == []`. 1987 1988 # Inputs 1989 1990 `a` 1991 1992 : 1\. Function argument 1993 1994 `b` 1995 1996 : 2\. Function argument 1997 */ 1998 mutuallyExclusive = a: b: length a == 0 || !(any (x: elem x a) b); 1999 2000 /** 2001 Concatenate all attributes of an attribute set. 2002 This assumes that every attribute of the set is a list. 2003 2004 # Inputs 2005 2006 `set` 2007 2008 : Attribute set with attributes that are lists 2009 2010 # Examples 2011 :::{.example} 2012 ## `lib.concatAttrValues` usage example 2013 2014 ```nix 2015 concatAttrValues { a = [ 1 2 ]; b = [ 3 ]; } 2016 => [ 1 2 3 ] 2017 ``` 2018 2019 ::: 2020 */ 2021 concatAttrValues = set: concatLists (attrValues set); 2022 2023 /** 2024 Replaces a list's nth element with a new element 2025 2026 # Inputs 2027 2028 `list` 2029 : Input list 2030 2031 `idx` 2032 : index to replace 2033 2034 `newElem` 2035 : new element to replace with 2036 2037 # Type 2038 2039 ``` 2040 replaceElemAt :: [a] -> int - b -> [a] 2041 ``` 2042 2043 # Examples 2044 :::{.example} 2045 ## `replaceElemAt` usage example 2046 2047 ```nix 2048 lib.replaceElemAt` [1 2 3] 0 "a" 2049 => ["a" 2 3] 2050 ``` 2051 2052 ::: 2053 */ 2054 replaceElemAt = 2055 list: idx: newElem: 2056 assert lib.assertMsg (idx >= 0 && idx < length list) 2057 "'lists.replaceElemAt' called with index ${toString idx} on a list of size ${toString (length list)}"; 2058 genList (i: if i == idx then newElem else elemAt list i) (length list); 2059}