MAJOR: create nt/primitives/bootstrap

This commit is contained in:
Emile Clark-Boman 2026-01-25 13:08:05 +10:00
parent db24747a47
commit 7e930193a9
27 changed files with 132 additions and 92 deletions

View file

@ -4,8 +4,8 @@
outputs = _: let
# nt depends on the mix subsystem for bootstrapping,
# we can fake its dependency on this mwahahahah
this.util = import ./nt/primitives/util/bootstrap.nix;
mix = import ./nt/primitives/mix {inherit this;};
bootstrap = import ./nt/primitives/bootstrap;
mix = import ./nt/primitives/mix {this = bootstrap;};
in
import ./nt {inherit mix;};
}

View file

@ -1,15 +0,0 @@
# WARNING: this file is strictly for bootstrapping nt
let
input = {inherit 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

View file

@ -0,0 +1,53 @@
## Primitives - /nt/primitives
All expressions in the `/nt/primitives` directory should have no dependencies on NixTypes
because **`/nt/primitives` is the dependency of NixTypes**! However for consistency in
the development process `/nt/primitives` is structured using the Mix Module subsystem
(`/nt/primitives/mix`).
*"But Emile..."* I hear you say! **How can primitives be a Mix module if Mix depends on primitives??**
Welp I'm sure I could have done some cool exploit of lazy evaluation in recursion,
*but I was alas lazy...* So the solution is **they can't... ;-;** But they can be pretty close!
Instead we have `/nt/primitives/bootstrap`! Which provides a **miniature dependency-free standard library.**
`import ./nt/primitives/bootstrap/default.nix` is passed as to `import ./nt/primitives/mix` which is
then passed to `import ./nt`.
Most importantly, `/nt/primitives/bootstrap/default.nix` contains the function `bootstrap:: Path | List | Attrs | Function -> Attrs`:
```nix
# et voilà!
let
this = bootstrap {inherit this bootstrap;} [
{
std = ./std/bootstrap.nix;
types = ./types/bootstrap.nix;
parse = ./parse/bootstrap.nix;
}
];
in
this;
```
**\~\~!!KABOOM !!\~\~**
Now **our primitives have a primitive module system!**
The init process looks like this:
```nix
# REF: flake.nix
{
outputs = _: let
# Step 1: Bootstrap and blast off (*zooommmmm whoooosshhhhh pppppeeeeeeewww*)
bootstrap = import ./nt/primitives/bootstrap;
# Step 2: Lie to Mix about its real identity (it's not ready for the truth...)
mix = import ./nt/primitives/mix {this = bootstrap;};
in
# Step 3: Actually import NixTypes
import ./nt {inherit mix;};
# Step 4: Like and subscripe!!1!11!!!!!
}
```
## A note on /nt/primitives/bootstrap
**None of these functions/types/etc are exported for users of NixTypes!** So they should
remain as simple and minimal as possible to avoid extra work maintaining.
Instead, **most of these will be reimplemented post-bootstrap to be NixType compatible**.

View file

@ -0,0 +1,39 @@
# WARNING: /nt/primitives/bootstrap cannot depend on mix!
# WARNING: This file is strictly for bootstrapping nt.
# WARNING: Use it with `import ./nt/primitives/bootstrap`
let
inherit
(builtins)
foldl'
isAttrs
isList
isPath
mapAttrs
;
# NOTE: bootstrap does the equivalent to mix's `include.public` option.
bootstrap = inputs: let
delegate = target:
# PATH
if isPath target
then import target inputs
# LIST
else if isList target
then target |> foldl' (acc: el: acc // delegate el) {}
# ATTRS
else if isAttrs target
then target |> mapAttrs (_: value: delegate value)
# FUNCTION (OR FAIL)
else target inputs;
in
delegate;
this = bootstrap {inherit this bootstrap;} [
{
std = ./std/bootstrap.nix;
types = ./types/bootstrap.nix;
parse = ./parse/bootstrap.nix;
}
];
in
this

View file

@ -0,0 +1,7 @@
# WARNING: /nt/primitives/bootstrap cannot depend on mix
# WARNING: this file is strictly for bootstrapping nt
{bootstrap, ...} @ inputs:
bootstrap inputs [
./parse.nix
./sig.nix
]

View file

@ -9,7 +9,7 @@
inherit
(this.std)
enfHasAttr
enfHasAttr'
enfHasAttrUnsafe
enfIsAttrs
;
in rec {
@ -19,7 +19,7 @@ in rec {
mkTrapdoorFn = key: decl:
assert enfHasAttr "default" decl "mkTrapdoorFn";
assert enfHasAttr' "unlock" decl "mkTrapdoorFn";
assert enfHasAttrUnsafe "unlock" decl "mkTrapdoorFn";
# return trapdoor function
(x: let
keys = attrNames decl.unlock;
@ -32,7 +32,7 @@ in rec {
mkTrapdoorSet = key: decl:
assert enfHasAttr "default" decl "mkTrapdoorSet";
assert enfHasAttr' "unlock" decl "mkTrapdoorSet";
assert enfHasAttrUnsafe "unlock" decl "mkTrapdoorSet";
# return trapdoor set
let
keys = attrNames decl.unlock;

View file

@ -0,0 +1,8 @@
## Primitive Standard Functions - /nt/primitives/bootstrap/std
>[!NOTE]
> This directory is dedicated to porting functions from `nixpkgs.pkgs.lib`.
**None of these functions/types/etc 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 will be reimplemented post-bootstrap to be NixType compatible**.

View file

@ -0,0 +1,11 @@
# WARNING: /nt/primitives/bootstrap cannot depend on mix
# WARNING: this file is strictly for bootstrapping nt
{bootstrap, ...} @ inputs:
bootstrap inputs [
./attrs.nix
./enforce.nix
./fn.nix
./list.nix
./num.nix
./string.nix
]

View file

@ -0,0 +1,8 @@
# WARNING: /nt/primitives/bootstrap cannot depend on mix
# WARNING: this file is strictly for bootstrapping nt
{bootstrap, ...} @ inputs:
bootstrap inputs [
./null.nix
./maybe.nix
./wrap.nix
]

View file

@ -2,6 +2,7 @@
mix.newMixture inputs (mixture: {
includes.public = [
./nt.nix
./bootstrap
];
submods.public = [
./mix

View file

@ -1,10 +0,0 @@
# WARNING: This file is strictly for bootstrapping nt
rec {
# NOTE: bootstrap does the equivalent to mix's `include.public` option.
# NOTE: It also provides itself as input to descendents.
bootstrap = args: paths: let
input = args // {inherit this bootstrap;};
this = paths |> builtins.foldl' (acc: el: acc // import el input) {};
in
this;
}

View file

@ -1,17 +0,0 @@
## 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.

View file

@ -1,28 +0,0 @@
# {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

View file

@ -1,2 +0,0 @@
{...}: {
}

View file

@ -1,8 +0,0 @@
{mix, ...} @ inputs:
mix.newMixture inputs (mixture: {
includes.public = [
./maybe.nix
./null.nix
./wrap.nix
];
})

View file

@ -1,7 +0,0 @@
{mix, ...} @ inputs:
mix.newMixture inputs (mixture: {
includes.public = [
./nt.nix
./trapdoor.nix
];
})