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
|
{this, ...}: let
|
||||||
inherit
|
inherit
|
||||||
(builtins)
|
(builtins)
|
||||||
hasAttr
|
|
||||||
typeOf
|
typeOf
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
@ -13,20 +12,12 @@
|
||||||
isTypeSig
|
isTypeSig
|
||||||
toTypeSig
|
toTypeSig
|
||||||
;
|
;
|
||||||
in rec {
|
in {
|
||||||
enfIsType = type: value: msg: let
|
enfIsType = type: value: msg: let
|
||||||
got = typeOf value;
|
got = typeOf value;
|
||||||
in
|
in
|
||||||
got == type || throw "${msg}: expected primitive nix type \"${type}\" but got \"${got}\"";
|
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:
|
enfIsClassSig = sig: msg:
|
||||||
isClassSig sig || throw "${msg}: given value \"${toString sig}\" of primitive nix type \"${typeOf sig}\" is not a valid Typeclass signature";
|
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:
|
enfIsNT = T: msg:
|
||||||
isNT T || throw "${msg}: expected nt compatible type but got \"${toString T}\" of primitive nix type \"${typeOf T}\"";
|
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:
|
enfImpls = type: T: msg:
|
||||||
impls type T || throw "${msg}: given type \"${toTypeSig T}\" does not implement typeclass \"${toTypeSig type}\"";
|
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