rec { inherit true false null functionArgs typeOf seq deepSeq bitAnd bitOr bitXor floor ceil; # from the minimal prelude abort = _: null null; # we ignore the provided message throw = abort; # same here head = xs: (__mn_list_match xs).head; tail = xs: (__mn_list_match xs).tail; __mn_matchAttr = f: as: f (__mn_attr_match as); __mn_matchList = f: xs: f (__mn_list_match xs); __mn_matchString = f: s: f (__mn_string_match s); __mn_foldr = op: nul: __mn_matchList ({ head, tail, empty }: if empty then nul else op head (__mn_foldr op nul tail)); # foldl' should really be strict. But if we do that (using seq), the # complexity of this function suddenly morphs from linear to # exponential, which is way worse than not actually being strict. foldl' = op: nul: __mn_matchList ({ head, tail, empty }: if empty then nul else let v = op nul head; in seq v (foldl' op v tail)); map = f: __mn_matchList ({ head, tail, empty }: if empty then [ ] else [ (f head) ] ++ map f tail); elem = x: any (y: x == y); elemAt = xs: n: assert n >= 0; let go = xs: n: if n == 0 then head xs else go (tail xs) (n - 1); in go xs n; length = __mn_matchList ({ head, tail, empty }: if empty then 0 else 1 + length tail); sort = __mn_mergesort; any = f: __mn_matchList ({ head, tail, empty }: !empty && (f head || any f tail)); all = f: __mn_matchList ({ head, tail, empty }: !empty -> (f head && all f tail)); concatLists = __mn_matchList ({ head, tail, empty }: if empty then [ ] else head ++ concatLists tail); concatMap = f: xss: concatLists (map f xss); concatStringsSep = sep: __mn_matchList ({ head, tail, empty }: if empty then "" else if tail == [ ] then head else head + sep + concatStringsSep sep tail); filter = f: __mn_matchList ({ head, tail, empty }: if empty then [ ] else (if f head then [ head ] else [ ]) ++ filter f tail); groupBy = f: xs: let update = x: acc: acc // { ${f x} = [ x ] ++ (acc.${f x} or [ ]); }; in __mn_foldr update { } xs; partition = f: groupBy (x: if f x then "right" else "wrong"); hasAttr = x: as: as ? ${x}; getAttr = x: as: as.${x}; attrNames = __mn_matchAttr ({ key, tail, empty, ... }: if empty then [ ] else [ key ] ++ attrNames tail); attrValues = __mn_matchAttr ({ head, tail, empty, ... }: if empty then [ ] else [ head ] ++ attrValues tail); mapAttrs = f: __mn_matchAttr ({ key, head, tail, empty }: if empty then { } else mapAttrs f tail // { ${key} = f key head; }); removeAttrs = __mn_foldr (x: as': __mn_attr_delete as' x); zipAttrsWith = f: ass: mapAttrs f (__mn_zipAttrs ass); catAttrs = x: __mn_matchList ({ head, tail, empty }: if empty then [ ] else (if head ? ${x} then [ head.${x} ] else [ ]) ++ catAttrs x tail); listToAttrs = __mn_foldr (attr: as': as' // { ${attr.name} = attr.value; }) { }; intersectAttrs = e1: e2: __mn_matchAttr ({ key, head, tail, empty }: if empty then { } else (if e2 ? ${key} then { ${key} = e2.${key}; } else { }) // intersectAttrs tail (__mn_attr_delete e2 key)) e1; lessThan = x: y: x < y; # documentation is misleading, not only for numbers add = x: y: x + y; mul = x: y: x * y; div = x: y: x / y; sub = x: y: x - y; genList = gen: n: let aux = off: if off >= n then [ ] else [ (gen off) ] ++ aux (off + 1); in aux 0; __mn_genericClosure = { operator, seen, startSet }: __mn_matchList ({ head, tail, empty }: if empty then [ ] else if seen head.key then __mn_genericClosure { inherit operator seen; startSet = tail; } else [ head ] ++ __mn_genericClosure { inherit operator; seen = k: k == head.key || seen k; startSet = tail ++ operator head; }) startSet; genericClosure = { operator, startSet }: __mn_genericClosure { inherit operator startSet; seen = _: false; }; isAttrs = e: typeOf e == "set"; isBool = e: typeOf e == "bool"; isFloat = e: typeOf e == "float"; isFunction = e: typeOf e == "lambda"; isInt = e: typeOf e == "int"; isList = e: typeOf e == "list"; isNull = e: typeOf e == "null"; isString = e: typeOf e == "string"; toString = e: if isAttrs e then if e ? __toString then e.__toString e else e.outPath else if isBool e then if e then "1" else "" else if isFloat e then __mn_float_toString e else if isInt e then __mn_int_toString e else if isList e then concatStringsSep " " (map toString e) else if isNull e then "" else if isString e then e else abort null; stringLength = __mn_matchString ({ head, tail, empty }: if empty then 0 else 1 + stringLength tail); substring = start: assert start >= 0; len: __mn_matchString ({ head, tail, empty }: if empty || len == 0 then "" else if start > 0 then substring (start - 1) len tail else head + substring 0 (len - 1) tail); replaceStrings = from: to: s: __mn_matchList ({ head, tail, empty }: let from = if empty then [ ] else [ head ] ++ tail; in __mn_matchList ({ head, tail, empty }: let to = if empty then [ ] else [ head ] ++ tail; in assert length from == length to; __mn_strings_replace from to s) to) from; __mn_strings_replace = subsFrom: subsTo: s: let go = __mn_strings_replace subsFrom subsTo; in __mn_strings_replace_aux go subsFrom subsTo s; __mn_strings_replace_aux = go: subsFrom: subsTo: s: __mn_matchList ({ head, tail, empty }: if empty then __mn_matchString ({ head, tail, empty }: if empty then "" else head + go tail) s else let subFrom = head; subsFrom' = tail; in __mn_matchList ({ head, tail, ... }: let subTo = head; subsTo' = tail; in if subFrom == "" then # We can only ask ourselves why, but it is so -- in Nix: # replaceStrings ["" "a"] ["X" "_"] "asdf" ~> "XaXsXdXfX" # and so we emulate this 'behavior' subTo + __mn_matchString ({ head, tail, empty }: if empty then "" else head + go tail) s else ({ ok, rest }: if ok then subTo + go rest else __mn_strings_replace_aux go subsFrom' subsTo' s) (__mn_string_chopPrefix subFrom s)) subsTo) subsFrom; __mn_string_chopPrefix = prefix: s: __mn_matchString ({ head, tail, empty }: if empty then { ok = true; rest = s; } else let prefix = head; prefix' = tail; in __mn_matchString ({ head, tail, empty }: if empty || prefix != head then { ok = false; rest = null; } else __mn_string_chopPrefix prefix' tail) s) prefix; __mn_string_drop = n: s: if n <= 0 then s else __mn_matchString ({ tail, empty, ... }: if empty then "" else __mn_string_drop (n - 1) tail) s; __mn_float_toString = x: let sign = x < 0; abs = __mn_abs x; int = floor abs; dec = __mn_nearestEven ((abs - int) * 1000000); in (if sign then "-" else "") + __mn_int_toString int + "." + __mn_int_toString dec; __mn_int_toString = x: (if x < 0 then "-" else "") + ( let d10 = __mn_quotRem (__mn_abs x) 10; in (if d10.quot != 0 then toString d10.quot else "") + (if d10.rem == 0 then "0" else if d10.rem == 1 then "1" else if d10.rem == 2 then "2" else if d10.rem == 3 then "3" else if d10.rem == 4 then "4" else if d10.rem == 5 then "5" else if d10.rem == 6 then "6" else if d10.rem == 7 then "7" else if d10.rem == 8 then "8" else if d10.rem == 9 then "9" else abort null) ); __mn_quotRem = x: y: let quot = x / y; in { inherit quot; rem = x - quot * y; }; __mn_abs = x: if x < 0 then -x else x; __mn_attr_insertNew = as: x: e: if x == null then { } else assert !(as ? ${x}); as // __mn_singleton x e; __mn_attr_has_aux = d: e: if typeOf d != "set" then false else __mn_attr_has_prim d e; __mn_attr_has = e: __mn_matchList ({ head, tail, empty }: if empty then true else if __mn_attr_has_aux e head then __mn_attr_has e.${head} tail else false); __mn_attr_select = e: __mn_matchList ({ head, tail, empty }: if empty then e else __mn_attr_select e.${head} tail); __mn_attr_selectOr = e: as: d: if __mn_attr_has e as then __mn_attr_select e as else d; __mn_assert = e1: e2: if e1 then e2 else abort null; __mn_consAttrs = as: acc: __mn_foldr (x: acc: acc // { ${x} = [ as.${x} ] ++ (acc.${x} or [ ]); }) acc (attrNames as); __mn_zipAttrs = __mn_foldr __mn_consAttrs { }; # Old merge sort algorithm, taken from GHC.Internal.Data.OldList. __mn_mergesort = cmp: xs: __mn_mergesort' cmp (__mn_singletons xs); __mn_singletons = map (x: [ x ]); __mn_mergesort' = cmp: xs: __mn_matchList ({ head, tail, empty }: if empty then [ ] else if tail == [ ] then head else __mn_mergesort' cmp (__mn_mergePairs cmp xs)) xs; __mn_mergePairs = cmp: __mn_matchList ({ head, tail, empty }: if empty then [ ] else let xs' = head; in __mn_matchList ({ head, tail, empty }: if empty then [ xs' ] else let ys' = head; xss' = tail; in [ (__mn_merge cmp xs' ys') ] ++ __mn_mergePairs cmp xss') tail); __mn_merge = cmp: xs: ys: __mn_matchList ({ head, tail, empty }: if empty then ys else let x = head; xs' = tail; in __mn_matchList ({ head, tail, empty }: if empty then xs else let y = head; ys' = tail; in if cmp y x then [ y ] ++ __mn_merge cmp xs ys' # y < x, i.e., x > y else [ x ] ++ __mn_merge cmp xs' ys) ys) xs; }