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