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;
}