segment util/util.nix into std/*
This commit is contained in:
parent
e3858bb932
commit
7c1dc8605f
10 changed files with 257 additions and 184 deletions
17
nt/primitives/std/README.md
Normal file
17
nt/primitives/std/README.md
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
## Primitive Standard Functions
|
||||
>[!NOTE]
|
||||
> This directory is dedicated to porting functions from `nixpkgs.pkgs.lib`.
|
||||
|
||||
The `/nt/primitives` directory should have no dependencies on NixTypes *(with one exception explained below)*.
|
||||
Thus the **NixTypes system must be constructed from a dependency-free standard library.**
|
||||
|
||||
This includes dependency on `this` (provided by the `nt.mix` module system)!
|
||||
`/nt/primitives` is structured as a `nt.mix` module, and hence `/nt/primitives/mix`
|
||||
must also depend on `/nt/primitives/std`. My point is, **`/nt/primitives/std`
|
||||
cannot use mix at all!**
|
||||
|
||||
### Internal Use Only
|
||||
**None of these functions are exported for users of NixTypes!** So they should
|
||||
remain as simple and minimal as possible to avoid extra work maintaining.
|
||||
Instead, **all of these functions will be reimplemented** post-bootstrap
|
||||
to be NixType compatible.
|
||||
73
nt/primitives/std/attrs.nix
Normal file
73
nt/primitives/std/attrs.nix
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
{...}: let
|
||||
inherit
|
||||
(builtins)
|
||||
attrNames
|
||||
elem
|
||||
elemAt
|
||||
filter
|
||||
hasAttr
|
||||
head
|
||||
length
|
||||
mapAttrs
|
||||
partition
|
||||
removeAttrs
|
||||
tail
|
||||
typeOf
|
||||
;
|
||||
in rec {
|
||||
enfIsAttrs = value: msg: let
|
||||
got = typeOf value;
|
||||
in
|
||||
got == "set" || throw "${msg}: expected primitive nix type \"set\" but got \"${got}\"";
|
||||
|
||||
# NOTE: doesn't check if xs is type set, use enfHasAttr instead
|
||||
enfHasAttrUnsafe = name: xs: msg:
|
||||
hasAttr name xs || throw "${msg}: missing required attribute \"${name}\"";
|
||||
|
||||
# NOTE: use enfHasAttr' if you can guarantee xs is type set
|
||||
enfHasAttr = name: xs: msg:
|
||||
enfIsAttrs xs msg && enfHasAttrUnsafe name xs msg;
|
||||
|
||||
mergeAttrsList = list: let
|
||||
# `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end`
|
||||
# Type: Int -> Int -> Attrs
|
||||
binaryMerge = start: end:
|
||||
# assert start < end; # Invariant
|
||||
if end - start >= 2
|
||||
then
|
||||
# If there's at least 2 elements, split the range in two, recurse on each part and merge the result
|
||||
# The invariant is satisfied because each half will have at least 1 element
|
||||
binaryMerge start (start + (end - start) / 2) // binaryMerge (start + (end - start) / 2) end
|
||||
else
|
||||
# Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly
|
||||
elemAt list start;
|
||||
in
|
||||
if list == []
|
||||
then
|
||||
# Calling binaryMerge as below would not satisfy its invariant
|
||||
{}
|
||||
else binaryMerge 0 (length list);
|
||||
|
||||
removeAttrsRec = paths: xs: let
|
||||
parts = partition (p: length p == 1) paths;
|
||||
here = parts.right;
|
||||
next = parts.wrong;
|
||||
in
|
||||
xs
|
||||
|> flipCurry removeAttrs here
|
||||
|> mapAttrs (name:
|
||||
if ! elem name next
|
||||
then id
|
||||
else
|
||||
next
|
||||
|> filter (x: head x == name)
|
||||
|> map tail
|
||||
|> removeAttrsRec);
|
||||
|
||||
filterAttrs = pred: xs:
|
||||
attrNames xs
|
||||
|> filter (name: ! pred name xs.${name})
|
||||
|> removeAttrs xs;
|
||||
|
||||
nameValuePair = name: value: {inherit name value;};
|
||||
}
|
||||
28
nt/primitives/std/default.nix
Normal file
28
nt/primitives/std/default.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# {mix, ...} @ inputs:
|
||||
# mix.newMixture inputs (mixture: {
|
||||
# includes.public = [
|
||||
# ./attrs.nix
|
||||
# ./fn.nix
|
||||
# ./list.nix
|
||||
# ./num.nix
|
||||
# ./string.nix
|
||||
# ];
|
||||
# })
|
||||
# WARNING: /nt/primitives/std cannot depend on mix
|
||||
# WARNING: this file is strictly for bootstrapping nt
|
||||
let
|
||||
# input = {inherit this;};
|
||||
this = {
|
||||
};
|
||||
# this =
|
||||
# import ./util.nix input
|
||||
# // import ./parse.nix input
|
||||
# // import ./trapdoor.nix input
|
||||
# // import ./null.nix input
|
||||
# // import ./maybe.nix input
|
||||
# // import ./wrap.nix input
|
||||
# // import ./enforce.nix input
|
||||
# // import ./sig.nix input
|
||||
# // import ./nt.nix input;
|
||||
in
|
||||
this
|
||||
2
nt/primitives/std/enforce.nix
Normal file
2
nt/primitives/std/enforce.nix
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
{...}: {
|
||||
}
|
||||
10
nt/primitives/std/fn.nix
Normal file
10
nt/primitives/std/fn.nix
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{...}: {
|
||||
id = x: x;
|
||||
flipCurry = f: a: b: f b a;
|
||||
|
||||
# not sure where else to put this...
|
||||
nullOr = f: x:
|
||||
if x != null
|
||||
then f x
|
||||
else x;
|
||||
}
|
||||
71
nt/primitives/std/list.nix
Normal file
71
nt/primitives/std/list.nix
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
{...}: let
|
||||
inherit
|
||||
(builtins)
|
||||
elemAt
|
||||
foldl'
|
||||
genList
|
||||
length
|
||||
;
|
||||
in rec {
|
||||
sublist = start: count: list: let
|
||||
len = length list;
|
||||
in
|
||||
genList (n: elemAt list (n + start)) (
|
||||
if start >= len
|
||||
then 0
|
||||
else if start + count > len
|
||||
then len - start
|
||||
else count
|
||||
);
|
||||
|
||||
take = count: sublist 0 count;
|
||||
|
||||
init = list:
|
||||
assert (list != []) || throw "lists.init: list must not be empty!";
|
||||
take (length list - 1) list;
|
||||
|
||||
last = list:
|
||||
assert (list != []) || throw "lists.last: list must not be empty!";
|
||||
elemAt list (length list - 1);
|
||||
|
||||
# REF: pkgs.lib.lists.reverseList
|
||||
reverse = xs: let
|
||||
l = length xs;
|
||||
in
|
||||
genList (n: elemAt xs (l - n - 1)) l;
|
||||
|
||||
# REF: pkgs.lib.lists.foldr
|
||||
foldr = op: nul: list: let
|
||||
len = length list;
|
||||
fold' = n:
|
||||
if n == len
|
||||
then nul
|
||||
else op (elemAt list n) (fold' (n + 1));
|
||||
in
|
||||
fold' 0;
|
||||
|
||||
# REF: pkgs.lib.lists.findFirstIndex [MODIFIED]
|
||||
firstIndexOf = x: list: let
|
||||
resultIndex =
|
||||
foldl' (
|
||||
index: el:
|
||||
if index < 0
|
||||
then
|
||||
# No match yet before the current index, we need to check the element
|
||||
if el == x
|
||||
then
|
||||
# We have a match! Turn it into the actual index to prevent future iterations from modifying it
|
||||
-index - 1
|
||||
else
|
||||
# Still no match, update the index to the next element (we're counting down, so minus one)
|
||||
index - 1
|
||||
else
|
||||
# There's already a match, propagate the index without evaluating anything
|
||||
index
|
||||
) (-1)
|
||||
list;
|
||||
in
|
||||
if resultIndex < 0
|
||||
then null
|
||||
else resultIndex;
|
||||
}
|
||||
30
nt/primitives/std/num.nix
Normal file
30
nt/primitives/std/num.nix
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
{...}: let
|
||||
inherit
|
||||
(builtins)
|
||||
elemAt
|
||||
genList
|
||||
length
|
||||
;
|
||||
in rec {
|
||||
inc = x: x + 1;
|
||||
dec = x: x - 1;
|
||||
|
||||
countEvensLeq = n: n / 2;
|
||||
countOddsLeq = n: (n + 1) / 2;
|
||||
|
||||
nats = genList (x: x);
|
||||
|
||||
odds = genList (x: 2 * x + 1);
|
||||
oddsLeq = n: countOddsLeq n |> genList (x: 2 * x + 1);
|
||||
|
||||
evens = genList (x: 2 * x);
|
||||
evensLeq = n: countEvensLeq n |> genList (x: 2 * x);
|
||||
|
||||
# WARNING: mapOdd/mapEven assuming the indexing set begins even (ie start counting from 0)
|
||||
mapOdd = f: list: oddsLeq (length list - 1) |> map (i: f (elemAt list i));
|
||||
mapEven = f: list: evensLeq (length list + 1) |> map (i: f (elemAt list i));
|
||||
|
||||
# WARNING: filterOdd/filterEven assuming the indexing set begins even (ie start counting from 0)
|
||||
filterOdd = mapOdd (x: x);
|
||||
filterEven = mapEven (x: x);
|
||||
}
|
||||
25
nt/primitives/std/string.nix
Normal file
25
nt/primitives/std/string.nix
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{...}: let
|
||||
inherit
|
||||
(builtins)
|
||||
genList
|
||||
match
|
||||
replaceStrings
|
||||
stringLength
|
||||
substring
|
||||
;
|
||||
in rec {
|
||||
stringElem = i: substring i 1;
|
||||
stringTake = substring 0;
|
||||
stringHead = stringTake 1;
|
||||
stringTail = x: x |> substring 1 (stringLength x - 1);
|
||||
stringInit = x: x |> stringTake 1;
|
||||
stringLast = x: stringElem (stringLength x - 1);
|
||||
|
||||
stringToCharacters = s: genList (p: substring p 1 s) (stringLength s);
|
||||
|
||||
escape = list: replaceStrings list (map (c: "\\${c}") list);
|
||||
escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
|
||||
|
||||
hasInfix = infix: content:
|
||||
match ".*${escapeRegex infix}.*" "${content}" != null;
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
{this, ...}: let
|
||||
inherit
|
||||
(builtins)
|
||||
hasAttr
|
||||
typeOf
|
||||
;
|
||||
|
||||
|
|
@ -13,20 +12,12 @@
|
|||
isTypeSig
|
||||
toTypeSig
|
||||
;
|
||||
in rec {
|
||||
in {
|
||||
enfIsType = type: value: msg: let
|
||||
got = typeOf value;
|
||||
in
|
||||
got == type || throw "${msg}: expected primitive nix type \"${type}\" but got \"${got}\"";
|
||||
|
||||
# NOTE: doesn't check if xs is type set, use enfHasAttr instead
|
||||
enfHasAttr' = name: xs: msg:
|
||||
hasAttr name xs || throw "${msg}: missing required attribute \"${name}\"";
|
||||
|
||||
# NOTE: use enfHasAttr' if you can guarantee xs is type set
|
||||
enfHasAttr = name: xs: msg:
|
||||
enfIsType "set" xs msg && enfHasAttr' name xs msg;
|
||||
|
||||
enfIsClassSig = sig: msg:
|
||||
isClassSig sig || throw "${msg}: given value \"${toString sig}\" of primitive nix type \"${typeOf sig}\" is not a valid Typeclass signature";
|
||||
|
||||
|
|
@ -36,8 +27,6 @@ in rec {
|
|||
enfIsNT = T: msg:
|
||||
isNT T || throw "${msg}: expected nt compatible type but got \"${toString T}\" of primitive nix type \"${typeOf T}\"";
|
||||
|
||||
# assert enfImpls "nt::&Maybe" T "nt::&Maybe.unwrap";
|
||||
# impls = type: T: assert enfIsNT T "nt.impls"; impls' type T;
|
||||
enfImpls = type: T: msg:
|
||||
impls type T || throw "${msg}: given type \"${toTypeSig T}\" does not implement typeclass \"${toTypeSig type}\"";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,172 +0,0 @@
|
|||
# TODO: move these declarations to a separate module (maybe?)
|
||||
{...}: let
|
||||
inherit
|
||||
(builtins)
|
||||
attrNames
|
||||
elem
|
||||
elemAt
|
||||
filter
|
||||
foldl'
|
||||
head
|
||||
genList
|
||||
length
|
||||
mapAttrs
|
||||
match
|
||||
partition
|
||||
removeAttrs
|
||||
replaceStrings
|
||||
stringLength
|
||||
substring
|
||||
tail
|
||||
;
|
||||
in rec {
|
||||
id = x: x;
|
||||
flipCurry = f: a: b: f b a;
|
||||
|
||||
inc = x: x + 1;
|
||||
dec = x: x - 1;
|
||||
|
||||
sublist = start: count: list: let
|
||||
len = length list;
|
||||
in
|
||||
genList (n: elemAt list (n + start)) (
|
||||
if start >= len
|
||||
then 0
|
||||
else if start + count > len
|
||||
then len - start
|
||||
else count
|
||||
);
|
||||
|
||||
take = count: sublist 0 count;
|
||||
|
||||
init = list:
|
||||
assert (list != []) || throw "lists.init: list must not be empty!";
|
||||
take (length list - 1) list;
|
||||
|
||||
last = list:
|
||||
assert (list != []) || throw "lists.last: list must not be empty!";
|
||||
elemAt list (length list - 1);
|
||||
|
||||
# REF: pkgs.lib.lists.reverseList
|
||||
reverse = xs: let
|
||||
l = length xs;
|
||||
in
|
||||
genList (n: elemAt xs (l - n - 1)) l;
|
||||
|
||||
# REF: pkgs.lib.lists.foldr
|
||||
foldr = op: nul: list: let
|
||||
len = length list;
|
||||
fold' = n:
|
||||
if n == len
|
||||
then nul
|
||||
else op (elemAt list n) (fold' (n + 1));
|
||||
in
|
||||
fold' 0;
|
||||
|
||||
# REF: pkgs.lib.lists.findFirstIndex [MODIFIED]
|
||||
firstIndexOf = x: list: let
|
||||
resultIndex =
|
||||
foldl' (
|
||||
index: el:
|
||||
if index < 0
|
||||
then
|
||||
# No match yet before the current index, we need to check the element
|
||||
if el == x
|
||||
then
|
||||
# We have a match! Turn it into the actual index to prevent future iterations from modifying it
|
||||
-index - 1
|
||||
else
|
||||
# Still no match, update the index to the next element (we're counting down, so minus one)
|
||||
index - 1
|
||||
else
|
||||
# There's already a match, propagate the index without evaluating anything
|
||||
index
|
||||
) (-1)
|
||||
list;
|
||||
in
|
||||
if resultIndex < 0
|
||||
then null
|
||||
else resultIndex;
|
||||
|
||||
nullOr = f: x:
|
||||
if x != null
|
||||
then f x
|
||||
else x;
|
||||
|
||||
stringElem = i: substring i 1;
|
||||
stringTake = substring 0;
|
||||
stringHead = stringTake 1;
|
||||
stringTail = x: x |> substring 1 (stringLength x - 1);
|
||||
stringInit = x: x |> stringTake 1;
|
||||
stringLast = x: stringElem (stringLength x - 1);
|
||||
|
||||
stringToCharacters = s: genList (p: substring p 1 s) (stringLength s);
|
||||
|
||||
escape = list: replaceStrings list (map (c: "\\${c}") list);
|
||||
escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
|
||||
|
||||
hasInfix = infix: content:
|
||||
match ".*${escapeRegex infix}.*" "${content}" != null;
|
||||
|
||||
countEvensLeq = n: n / 2;
|
||||
countOddsLeq = n: (n + 1) / 2;
|
||||
|
||||
nats = genList id;
|
||||
|
||||
odds = genList (x: 2 * x + 1);
|
||||
oddsLeq = n: countOddsLeq n |> genList (x: 2 * x + 1);
|
||||
|
||||
evens = genList (x: 2 * x);
|
||||
evensLeq = n: countEvensLeq n |> genList (x: 2 * x);
|
||||
|
||||
# WARNING: mapOdd/mapEven assuming the indexing set begins even (ie start counting from 0)
|
||||
mapOdd = f: list: oddsLeq (length list - 1) |> map (i: f (elemAt list i));
|
||||
mapEven = f: list: evensLeq (length list + 1) |> map (i: f (elemAt list i));
|
||||
|
||||
# WARNING: filterOdd/filterEven assuming the indexing set begins even (ie start counting from 0)
|
||||
filterOdd = mapOdd id;
|
||||
filterEven = mapEven id;
|
||||
|
||||
mergeAttrsList = list: let
|
||||
# `binaryMerge start end` merges the elements at indices `index` of `list` such that `start <= index < end`
|
||||
# Type: Int -> Int -> Attrs
|
||||
binaryMerge = start: end:
|
||||
# assert start < end; # Invariant
|
||||
if end - start >= 2
|
||||
then
|
||||
# If there's at least 2 elements, split the range in two, recurse on each part and merge the result
|
||||
# The invariant is satisfied because each half will have at least 1 element
|
||||
binaryMerge start (start + (end - start) / 2) // binaryMerge (start + (end - start) / 2) end
|
||||
else
|
||||
# Otherwise there will be exactly 1 element due to the invariant, in which case we just return it directly
|
||||
elemAt list start;
|
||||
in
|
||||
if list == []
|
||||
then
|
||||
# Calling binaryMerge as below would not satisfy its invariant
|
||||
{}
|
||||
else binaryMerge 0 (length list);
|
||||
|
||||
removeAttrsRec = paths: xs: let
|
||||
parts = partition (p: length p == 1) paths;
|
||||
here = parts.right;
|
||||
next = parts.wrong;
|
||||
in
|
||||
xs
|
||||
|> flipCurry removeAttrs here
|
||||
|> mapAttrs (name:
|
||||
if ! elem name next
|
||||
then id
|
||||
else
|
||||
next
|
||||
|> filter (x: head x == name)
|
||||
|> map tail
|
||||
|> removeAttrsRec);
|
||||
|
||||
filterAttrs = pred: xs:
|
||||
attrNames xs
|
||||
|> filter (name: ! pred name xs.${name})
|
||||
|> removeAttrs xs;
|
||||
|
||||
nameValuePair = name: value: {inherit name value;};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue