holy mega commit
there's no way this runs :(
This commit is contained in:
parent
6537f0cdaa
commit
affeace641
12 changed files with 639 additions and 0 deletions
6
nt/default.nix
Normal file
6
nt/default.nix
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
{mix, ...} @ inputs:
|
||||||
|
mix.newMixture inputs (mixture: {
|
||||||
|
includes.public = [
|
||||||
|
./primitives
|
||||||
|
];
|
||||||
|
})
|
||||||
9
nt/primitives/README.md
Normal file
9
nt/primitives/README.md
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
OVERRIDE ALL NIX BUILTINS
|
||||||
|
ie at the root of your flake you modify what builtins is / use an input
|
||||||
|
you should never have to depend on builtins or pkgs.lib, only nt
|
||||||
|
|
||||||
|
|
||||||
|
Naming Convention
|
||||||
|
|
||||||
|
MyName -> Type
|
||||||
|
MyName' -> Typeclass
|
||||||
10
nt/primitives/default.nix
Normal file
10
nt/primitives/default.nix
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{mix, ...} @ inputs:
|
||||||
|
mix.newMixture inputs (mixture: {
|
||||||
|
includes.public = [
|
||||||
|
./nt.nix
|
||||||
|
];
|
||||||
|
imports.public = [
|
||||||
|
# TODO: make ./util private
|
||||||
|
./util
|
||||||
|
];
|
||||||
|
})
|
||||||
104
nt/primitives/nt.nix
Normal file
104
nt/primitives/nt.nix
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
{this, ...}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
isFunction
|
||||||
|
length
|
||||||
|
mapAttrs
|
||||||
|
partition
|
||||||
|
;
|
||||||
|
|
||||||
|
inherit
|
||||||
|
(this.util)
|
||||||
|
enfType
|
||||||
|
enfIsClassSig
|
||||||
|
flipCurry
|
||||||
|
hasAttrAt
|
||||||
|
mkTrapdoorSet
|
||||||
|
ntTrapdoorKey
|
||||||
|
parseClassSig
|
||||||
|
projectOnto
|
||||||
|
removeAttrsRec
|
||||||
|
typeSig
|
||||||
|
Wrap
|
||||||
|
;
|
||||||
|
|
||||||
|
recdef = def: let
|
||||||
|
Self = def Self;
|
||||||
|
in
|
||||||
|
Self;
|
||||||
|
|
||||||
|
classDecl = {
|
||||||
|
derive = Wrap [];
|
||||||
|
ops = Wrap {};
|
||||||
|
};
|
||||||
|
|
||||||
|
unwrapBuilder = builder: Self:
|
||||||
|
if isFunction builder
|
||||||
|
then builder Self
|
||||||
|
else builder;
|
||||||
|
|
||||||
|
parseDecl = base: decl:
|
||||||
|
assert enfType "parseDecl" "set" decl;
|
||||||
|
# ^^^^ "Type declaration must be provided as an attribute set, got "${typeOf decl}" instead!"
|
||||||
|
decl |> projectOnto base;
|
||||||
|
|
||||||
|
# Algorithm: given a full set of ops, iterate each op and
|
||||||
|
# IF IT MATCHES A DERIVE BY FULL NAMESPACE
|
||||||
|
# THEN remove it from state.req
|
||||||
|
# ELSE IF IT IS SPECIFIED BY NAMESPACE
|
||||||
|
# THEN add it to a list of all invalid ops (errors)
|
||||||
|
# ELSE add it to a list of ops belonging solely to self
|
||||||
|
parseOps = ops: req: let
|
||||||
|
reqPaths =
|
||||||
|
req
|
||||||
|
|> mapAttrs (name: let
|
||||||
|
segs = parseClassSig name;
|
||||||
|
in
|
||||||
|
value: segs ++ [value]);
|
||||||
|
|
||||||
|
# XXX: TODO: having to specify the full namespace sucks :(
|
||||||
|
|
||||||
|
matches = partition (flipCurry hasAttrAt ops) reqPaths;
|
||||||
|
|
||||||
|
pathsMissing = matches.wrong;
|
||||||
|
opsSelf = removeAttrsRec matches.right ops;
|
||||||
|
opsDerived = removeAttrsRec matches.wrong ops;
|
||||||
|
in {
|
||||||
|
inherit opsSelf opsDerived pathsMissing;
|
||||||
|
success = length pathsMissing == 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkClass = sig: decl:
|
||||||
|
assert enfIsClassSig "mkClass" sig; let
|
||||||
|
allDerivedClasses =
|
||||||
|
decl.derive
|
||||||
|
|> map (class: typeSig class ++ class.${ntTrapdoorKey}.derive);
|
||||||
|
|
||||||
|
parseResult = parseOps decl.ops decl.req;
|
||||||
|
inherit
|
||||||
|
(parseResult)
|
||||||
|
opsSelf
|
||||||
|
opsDerived
|
||||||
|
;
|
||||||
|
in
|
||||||
|
# XXX: WARNING: classes currently *shouldn't* be able to inherit ops (i think?)
|
||||||
|
assert parseResult.success || throw "TODO";
|
||||||
|
mkTrapdoorSet {
|
||||||
|
default = opsSelf;
|
||||||
|
unlock = {
|
||||||
|
# TODO: rename derive to deriveSigs (EXCEPT in the classDecl)
|
||||||
|
${ntTrapdoorKey} = {
|
||||||
|
inherit sig;
|
||||||
|
derive = allDerivedClasses;
|
||||||
|
ops = {${sig} = opsSelf;} // opsDerived;
|
||||||
|
req = null; # XXX: TODO make it more advanced
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
Class = sig: builder:
|
||||||
|
recdef (Self:
|
||||||
|
unwrapBuilder builder Self
|
||||||
|
|> parseDecl classDecl
|
||||||
|
|> mkClass sig);
|
||||||
|
}
|
||||||
12
nt/primitives/util/default.nix
Normal file
12
nt/primitives/util/default.nix
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
{mix, ...} @ inputs:
|
||||||
|
mix.newMixture inputs (mixture: {
|
||||||
|
includes.public = [
|
||||||
|
./enforce.nix
|
||||||
|
./nt.nix
|
||||||
|
./parse.nix
|
||||||
|
./sig.nix
|
||||||
|
./trapdoor.nix
|
||||||
|
./util.nix
|
||||||
|
./wrap.nix
|
||||||
|
];
|
||||||
|
})
|
||||||
36
nt/primitives/util/enforce.nix
Normal file
36
nt/primitives/util/enforce.nix
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
{this, ...}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
hasAttr
|
||||||
|
typeOf
|
||||||
|
;
|
||||||
|
|
||||||
|
inherit
|
||||||
|
(this)
|
||||||
|
isClassSig
|
||||||
|
isNT
|
||||||
|
isTypeSig
|
||||||
|
;
|
||||||
|
in rec {
|
||||||
|
enfType = msg: type: value: 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' = msg: name: xs:
|
||||||
|
hasAttr name xs || throw "${msg}: missing required attribute \"${name}\"";
|
||||||
|
|
||||||
|
# NOTE: use enfHasAttr' if you can guarantee xs is type set
|
||||||
|
enfHasAttr = msg: name: xs:
|
||||||
|
enfType "set" xs msg && enfHasAttr' name xs msg;
|
||||||
|
|
||||||
|
enfIsClassSig = msg: sig:
|
||||||
|
isClassSig sig || throw "${msg}: given value \"${toString sig}\" of primitive nix type \"${typeOf sig}\" is not a valid Typeclass signature";
|
||||||
|
|
||||||
|
enfIsTypeSig = msg: sig:
|
||||||
|
isTypeSig sig || throw "${msg}: given value \"${toString sig}\" of primitive nix type \"${typeOf sig}\" is not a valid Type signature";
|
||||||
|
|
||||||
|
enfIsNT = msg: T:
|
||||||
|
isNT T || throw "${msg}: expected nt compatible type but got \"${toString T}\" of primitive nix type \"${typeOf T}\"";
|
||||||
|
}
|
||||||
59
nt/primitives/util/nt.nix
Normal file
59
nt/primitives/util/nt.nix
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
{this, ...}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
all
|
||||||
|
attrNames
|
||||||
|
elem
|
||||||
|
isAttrs
|
||||||
|
;
|
||||||
|
|
||||||
|
inherit
|
||||||
|
(this)
|
||||||
|
enfIsNT
|
||||||
|
ntTrapdoorKey
|
||||||
|
openTrapdoorFn
|
||||||
|
toTypeSig
|
||||||
|
;
|
||||||
|
in rec {
|
||||||
|
# check if a value is an nt type/class
|
||||||
|
isNT = T: let
|
||||||
|
content = openTrapdoorFn ntTrapdoorKey T;
|
||||||
|
names = attrNames content;
|
||||||
|
in
|
||||||
|
isAttrs content
|
||||||
|
&& all (name: elem name names) ["sig" "derive" "ops" "req"];
|
||||||
|
|
||||||
|
isNixClass = T: let
|
||||||
|
content = openTrapdoorFn ntTrapdoorKey T;
|
||||||
|
in
|
||||||
|
isAttrs content
|
||||||
|
&& attrNames content == ["sig" "derive" "ops" "req"];
|
||||||
|
|
||||||
|
isNixType = T: let
|
||||||
|
content = openTrapdoorFn ntTrapdoorKey T;
|
||||||
|
in
|
||||||
|
isAttrs content
|
||||||
|
&& attrNames content == ["instance" "sig" "derive" "ops" "req"]
|
||||||
|
&& content.instance == false;
|
||||||
|
|
||||||
|
isNixTypeInstance = T: let
|
||||||
|
content = openTrapdoorFn ntTrapdoorKey T;
|
||||||
|
in
|
||||||
|
isAttrs content
|
||||||
|
&& attrNames content == ["instance" "sig" "derive" "ops" "req"]
|
||||||
|
&& content.instance == true;
|
||||||
|
|
||||||
|
# check if a type/class implements a signature
|
||||||
|
# NOTE: unsafe variant, use typeSig if you can't guarantee `isNT T` holds
|
||||||
|
impls' = type: T: elem (toTypeSig type) T.${ntTrapdoorKey}.derive;
|
||||||
|
|
||||||
|
# NOTE safe variant, use impls' if you can guarantee `isNT T` holds
|
||||||
|
impls = type: T: assert enfIsNT "nt.impls" T; impls' type T;
|
||||||
|
|
||||||
|
# check if a type/class implements a signature
|
||||||
|
# NOTE: unsafe variant, use `is` if you can't guarantee `isNT T` holds
|
||||||
|
is' = type: T: T.${ntTrapdoorKey}.sig == toTypeSig type;
|
||||||
|
|
||||||
|
# NOTE safe variant, use `is'` if you can guarantee `isNT T` holds
|
||||||
|
is = type: T: assert enfIsNT "nt.is" T; is' type T;
|
||||||
|
}
|
||||||
58
nt/primitives/util/parse.nix
Normal file
58
nt/primitives/util/parse.nix
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
{this, ...}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
foldl'
|
||||||
|
hasAttr
|
||||||
|
isAttrs
|
||||||
|
;
|
||||||
|
|
||||||
|
inherit
|
||||||
|
(this)
|
||||||
|
enfType
|
||||||
|
is
|
||||||
|
Wrap
|
||||||
|
;
|
||||||
|
in rec {
|
||||||
|
# form: getAttrAt :: list string -> set -> null | Wrap Any
|
||||||
|
# given path as a list of strings, return that value of an
|
||||||
|
# attribute set at that path
|
||||||
|
getAttrAt = path: xs:
|
||||||
|
assert enfType "set" xs "getAttrAt";
|
||||||
|
foldl' (left: right:
|
||||||
|
if left != null && isAttrs left.value && hasAttr right left.value
|
||||||
|
then Wrap left.value.${right}
|
||||||
|
else null)
|
||||||
|
(Wrap xs)
|
||||||
|
path;
|
||||||
|
|
||||||
|
# form: hasAttrAt :: list string -> set -> bool
|
||||||
|
# given path as a list of strings, return that value of an
|
||||||
|
# attribute set at that path
|
||||||
|
hasAttrAt = path: xs:
|
||||||
|
assert enfType "set" xs "hasAttrAt";
|
||||||
|
getAttrAt path xs != null; # NOTE: inefficient (im lazy)
|
||||||
|
|
||||||
|
# Alternative to mapAttrsRecursiveCond
|
||||||
|
# Allows mapping directly from a child path
|
||||||
|
recmap = let
|
||||||
|
recmapFrom = path: f: T:
|
||||||
|
if builtins.isAttrs T && ! is Wrap T
|
||||||
|
then builtins.mapAttrs (attr: leaf: recmapFrom (path ++ [attr]) f leaf) T
|
||||||
|
else f path T;
|
||||||
|
in
|
||||||
|
recmapFrom [];
|
||||||
|
|
||||||
|
projectOnto = f: dst: src:
|
||||||
|
dst
|
||||||
|
|> recmap
|
||||||
|
(path: dstLeaf: let
|
||||||
|
srcLeaf = getAttrAt path src;
|
||||||
|
newLeaf =
|
||||||
|
if srcLeaf != null
|
||||||
|
then srcLeaf
|
||||||
|
else dstLeaf;
|
||||||
|
in
|
||||||
|
if is Wrap newLeaf
|
||||||
|
then newLeaf.value
|
||||||
|
else newLeaf);
|
||||||
|
}
|
||||||
103
nt/primitives/util/sig.nix
Normal file
103
nt/primitives/util/sig.nix
Normal file
|
|
@ -0,0 +1,103 @@
|
||||||
|
{this, ...}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
isString
|
||||||
|
split
|
||||||
|
stringLength
|
||||||
|
;
|
||||||
|
|
||||||
|
inherit
|
||||||
|
(this)
|
||||||
|
enfIsNT
|
||||||
|
filterEven
|
||||||
|
init
|
||||||
|
last
|
||||||
|
ntTrapdoorKey
|
||||||
|
nullOr
|
||||||
|
stringHead
|
||||||
|
stringTail
|
||||||
|
;
|
||||||
|
in rec {
|
||||||
|
parseSig = sig: let
|
||||||
|
result = split "::" sig |> filterEven;
|
||||||
|
in
|
||||||
|
if last result == ""
|
||||||
|
then null
|
||||||
|
else result;
|
||||||
|
|
||||||
|
isTypeName = name: stringHead name != "&";
|
||||||
|
isClassName = name: stringHead name == "&" && stringLength name != 1;
|
||||||
|
|
||||||
|
isTypeSig = sig:
|
||||||
|
parseSig sig |> nullOr (result: last result |> isTypeName);
|
||||||
|
|
||||||
|
isClassSig = sig:
|
||||||
|
parseSig sig |> nullOr (result: last result |> isClassSig);
|
||||||
|
|
||||||
|
parseTypeSig = sig: let
|
||||||
|
result = parseSig sig;
|
||||||
|
in
|
||||||
|
if result != null && (last result |> isTypeName)
|
||||||
|
then result
|
||||||
|
else null;
|
||||||
|
|
||||||
|
parseClassSig = sig: let
|
||||||
|
result = parseSig sig;
|
||||||
|
in
|
||||||
|
if result != null && (last result |> isClassName)
|
||||||
|
then init result ++ (last result |> stringTail)
|
||||||
|
else null;
|
||||||
|
|
||||||
|
# NOTE: unsafe variant, use typeSig if you can't guarantee `isNT T` holds
|
||||||
|
typeSig' = T: T.${ntTrapdoorKey}.sig;
|
||||||
|
|
||||||
|
# NOTE: safe variant, use typeSig' if you can guarantee `isNT T` holds
|
||||||
|
typeSig = T: assert enfIsNT "nt.typeSig" T; typeSig' T;
|
||||||
|
|
||||||
|
toTypeSig = x:
|
||||||
|
if isString x
|
||||||
|
then x
|
||||||
|
else typeSig x;
|
||||||
|
|
||||||
|
# NOTE: we're testing how similar `list` is to `toTypeSig type` (non-commutative)
|
||||||
|
# NOTE: we measure similarity in the reverse order (ie end of signature is most important)
|
||||||
|
# sigSimilarity = type: list: let
|
||||||
|
# # XXX: TODO: mkClass must enforce that type names can't begin with &
|
||||||
|
# trimClassPrefix = sig:
|
||||||
|
# if stringHead sig == "&"
|
||||||
|
# then stringTail sig
|
||||||
|
# else sig;
|
||||||
|
# S = toTypeSig type |> parseTypeSig |> map trimClassPrefix;
|
||||||
|
|
||||||
|
# progress = l: x: let
|
||||||
|
# index = firstIndexOf x l;
|
||||||
|
# in
|
||||||
|
# if index == null
|
||||||
|
# then []
|
||||||
|
# else sublist index (length l - index) l;
|
||||||
|
|
||||||
|
# op = state: el: let
|
||||||
|
# acc' = progress state.acc el;
|
||||||
|
# in
|
||||||
|
# # Continue progression in healthy condition
|
||||||
|
# if state.acc != [] && acc' != []
|
||||||
|
# then {
|
||||||
|
# score = state.score + 1;
|
||||||
|
# acc = acc';
|
||||||
|
# }
|
||||||
|
# # We didn't match the final element (ABORT with score=0)
|
||||||
|
# else if state.score == 0
|
||||||
|
# then {
|
||||||
|
# score = 0;
|
||||||
|
# acc = [];
|
||||||
|
# }
|
||||||
|
# # We didn't match this element but maybe next time :)
|
||||||
|
# else state;
|
||||||
|
# in
|
||||||
|
# list
|
||||||
|
# |> foldr op {
|
||||||
|
# score = 0;
|
||||||
|
# acc = reverse S;
|
||||||
|
# }
|
||||||
|
# |> getAttr "score";
|
||||||
|
}
|
||||||
60
nt/primitives/util/trapdoor.nix
Normal file
60
nt/primitives/util/trapdoor.nix
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
{this, ...}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
attrNames
|
||||||
|
elem
|
||||||
|
isFunction
|
||||||
|
;
|
||||||
|
|
||||||
|
inherit
|
||||||
|
(this)
|
||||||
|
enfHasAttr
|
||||||
|
enfHasAttr'
|
||||||
|
enfIsType
|
||||||
|
;
|
||||||
|
|
||||||
|
masterkey = "_''traps''_";
|
||||||
|
in rec {
|
||||||
|
defaultTrapdoorKey = "_'";
|
||||||
|
mkTrapdoorKey = id: "${defaultTrapdoorKey}${id}";
|
||||||
|
ntTrapdoorKey = mkTrapdoorKey "nt";
|
||||||
|
|
||||||
|
mkTrapdoorFn = key: decl:
|
||||||
|
assert enfHasAttr "default" decl "mkTrapdoorFn";
|
||||||
|
assert enfHasAttr' "unlock" decl "mkTrapdoorFn";
|
||||||
|
# return trapdoor function
|
||||||
|
(x: let
|
||||||
|
keys = attrNames decl.unlock;
|
||||||
|
in
|
||||||
|
if elem key keys
|
||||||
|
then decl.unlock.${key}
|
||||||
|
else if key == masterkey
|
||||||
|
then keys
|
||||||
|
else decl.default);
|
||||||
|
|
||||||
|
mkTrapdoorSet = key: decl:
|
||||||
|
assert enfHasAttr "default" decl "mkTrapdoorSet";
|
||||||
|
assert enfHasAttr' "unlock" decl "mkTrapdoorSet";
|
||||||
|
# return trapdoor set
|
||||||
|
let
|
||||||
|
keys = attrNames decl.unlock;
|
||||||
|
in
|
||||||
|
decl.default
|
||||||
|
// {
|
||||||
|
${key} = decl.unlock.${key};
|
||||||
|
${masterkey} = keys;
|
||||||
|
};
|
||||||
|
revealTrapdoors = openTrapdoor masterkey;
|
||||||
|
|
||||||
|
openTrapdoorFn = key: f: f key;
|
||||||
|
|
||||||
|
openTrapdoorSet = key: xs: xs.${key};
|
||||||
|
|
||||||
|
# TODO: implement a function called enfIsTypeAny (for cases like this where it might be function or set)
|
||||||
|
openTrapdoor = key: T:
|
||||||
|
if isFunction T
|
||||||
|
then openTrapdoorFn key T
|
||||||
|
else
|
||||||
|
assert enfIsType "set" T "openTrapdoor";
|
||||||
|
openTrapdoorSet key T;
|
||||||
|
}
|
||||||
154
nt/primitives/util/util.nix
Normal file
154
nt/primitives/util/util.nix
Normal file
|
|
@ -0,0 +1,154 @@
|
||||||
|
# TODO: move these declarations to a separate module (maybe?)
|
||||||
|
{...}: let
|
||||||
|
inherit
|
||||||
|
(builtins)
|
||||||
|
elem
|
||||||
|
elemAt
|
||||||
|
filter
|
||||||
|
foldl'
|
||||||
|
head
|
||||||
|
genList
|
||||||
|
length
|
||||||
|
mapAttrs
|
||||||
|
partition
|
||||||
|
removeAttrs
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
28
nt/primitives/util/wrap.nix
Normal file
28
nt/primitives/util/wrap.nix
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
{this, ...}: let
|
||||||
|
inherit
|
||||||
|
(this)
|
||||||
|
ntTrapdoorKey
|
||||||
|
mkTrapdoorFn
|
||||||
|
mkTrapdoorSet
|
||||||
|
;
|
||||||
|
in {
|
||||||
|
# NOTE: Wrap is used to simplify parsing Type/Class declarations
|
||||||
|
# NOTE: and therefore must be implemented manually
|
||||||
|
Wrap = let
|
||||||
|
meta = instance: {
|
||||||
|
inherit instance;
|
||||||
|
sig = "nt::Wrap";
|
||||||
|
derive = [];
|
||||||
|
ops = {};
|
||||||
|
req = {};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
mkTrapdoorFn ntTrapdoorKey {
|
||||||
|
default = value:
|
||||||
|
mkTrapdoorSet ntTrapdoorKey {
|
||||||
|
default = {inherit value;};
|
||||||
|
unlock = meta true;
|
||||||
|
};
|
||||||
|
unlock = meta false;
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue