nt/README.md

213 lines
5.5 KiB
Markdown
Raw Permalink Normal View History

2026-01-28 17:27:48 +10:00
# ❄ NixTypes (nt) ❄
2026-01-28 18:42:33 +10:00
<p align="center">Because Nix never held your hand. It shot off your fingers and spat out God's longest stack trace</p>
2025-12-14 14:35:15 +10:00
2026-01-28 17:27:48 +10:00
>[!WARNING]
> ✨ **Under Construction** ✨
2026-02-09 09:36:50 +10:00
>
2026-01-28 18:04:58 +10:00
> NixTypes is **quite** a large project to do alone, but it's been staring at me for the last 12 months.
2026-01-28 17:27:48 +10:00
> If you're interested feel free to contact me and/or submit pull requests :yellow_heart::yellow_heart:
2026-02-09 09:36:50 +10:00
>
2026-01-28 17:27:48 +10:00
> **Be not afraid!** It's only a matter of time until NixTypes is ready for use!
2025-12-14 14:35:15 +10:00
2026-02-09 09:36:50 +10:00
>[!CAUTION]
> The current syntax is quite ugly and **no it's not final.** NixTypes is in an
> **extremely experimental** prototyping stage!
2026-01-28 17:27:48 +10:00
## 💙 Huh?
Nix has no type system duh!? Sure that's fine for configuring your distro,
but what about developing in Nix itself? The code people write tends to be unnecessarily complex
or full or bugs. Nix needs standards, and NixTypes gives you those.
## :rainbow: More Than Types
NixTypes isn't *exactly* just a type system. **You didn't think I'd give you types
then say goodbye did you?** Then we'd be right back where we started... Instead
there's a whole standard library built from the ashes.
Some of the sweet sweet batteries included:
1. **Pattern Matching** (finally!!)
2. **Attribute Set Parsing**
3. **Pretty Printing** (no more `builtins.toString` errors)
4. **A Module System** (say goodbye to managing all your `imports`)
5. **Types, Types, & More Types** (Maybe/Some/None, Monads, Tree, Rose, etc)
2026-01-28 23:54:53 +10:00
6. **Support For Pipe Operators** (cleaner code with `<|` and `|>` )
2026-01-28 18:04:58 +10:00
2026-01-28 18:42:33 +10:00
## Types For Humans
2026-01-29 00:11:33 +10:00
Let's design a `Result` type using the NixTypes system!
We'll start simple and use the following attribute set
as our naive basis:
```nix
Result = success: value:
assert builtins.isBool success;
{ inherit success value; };
};
```
We can
```nix
let
inherit (nt)
print
toString
Bool
Fn
Type
;
in rec {
Result = Type (Self: {
ops = {
# we need to make a constructor
mk = Fn [Bool Any] Self (success: value: { inherit success value; });
# create some alternative constructors
mkSuccess = Self.mk true;
mkFail = Self.mk false;
isSuccess = self: self.success;
isFail = self: ! self.success;
unwrap = self: self.value;
};
});
# Example Usage:
tryGetAttr = name: attrs: let
success = attrs ? name;
value = attrs.${name} or "AttrSet missing attribute name \"${name}\"";
in
Result success value;
# prints myAttr and returns the default value (same as builtins.trace!)
printMyAttr = attrs: default: let
result = tryGetAttr "myAttr";
in
if result.isSuccess
then print (toString result.unwrap) default
else default;
}
```
Now let's try something a little harder and try to make our
`Result` type act more like Rust's `std::result` crate:
```rust
enum Result<T, E> {
Ok(T),
Err(E),
}
```
>[!TODO]
> I'm sleepy and I'll finish this in the morning...
2026-01-28 18:42:33 +10:00
## Types (Not) For Humans
Let's define a Maybe type in two diferent ways:
1. As a polymorphic type `PolyMaybe := Some | None` for monads `Some` and `None`
2. And as an lax-idempotent (KockZöberlein) monad `KZMaybe` with states `KZSome` and `KZNone`
```nix
let
inherit
(nt)
mk
enfNotNull
Type
Sum
Monad'
KZMonad' # apostrophe implies typeclass
;
in rec {
# === METHOD 1:
Some = Type {
ops = {
Monad'.result = value: mk { inherit value; };
Monad'.unwrap = self: self.value;
Monad'.bind = self: f: f self.value;
};
impls = [ Monad' ];
};
None = Type {
ops.mk = mk {
Monad'.result = mk {};
Monad'.unwrap = _: null;
Monad'.bind = _: _: null;
};
impls = [ Monad' ];
};
PolyMaybe = Sum [Some None];
# === METHOD 2:
KZSome = value:
assert enfNotNull value "KZSome value";
KZMaybe;
KZNone = KZMaybe null;
KZMaybe = Type {
ops = {
# Lift a value INTO the monadic context
# NOTE: because KZMaybe implements KZMonad' lax-idempotence
# NOTE: is automatically added to the result operation
Monad'.result = value: mk { inherit value; };
# Lift a value OUT OF the monadic context
Monad'.unwrap = self: self.value;
# Alter a value inside the monadic context
Monad'.bind = f: x: f x;
};
impls = [ KZMonad' ];
};
}
```
2026-01-28 18:04:58 +10:00
### ❄🎁 Parse the Parcel
*Close your eyes with me ok? MMmmmmmmmmm yes just like that...* **NOW** imagine
you're a moderately depressed and very sleepy programmer. You're reading a README.md
for a type system in Nix, I doubt it's that hard. Then **whoooshhh boooom**, Zeus
himself hath commended thee to fulfill his greatest ambition **OR DIE!!** You must
write a function taking an attribute set of the structure
```nix
{
a = { # optional
b = { # optional
c = ...; # optional (any type, default: null)
d = ...; # optional (string, default: "hola")
};
};
e = { # required
f = ...; # required (path)
g = ...; # optional (function, default: (x: x))
h = ...; # optional (string, default: "null")
};
}
```
Not having a great day now are you? It's doable and not overtly difficult,
but coming back in a couple months you'd have to decipher your solution.
**NOW BEHOLD:**
```nix
# nix made simple <3
2026-01-28 23:54:53 +10:00
let
inherit
(nt)
projectOnto
missing
verify
;
in
{
f = attrs:
attrs
|> projectOnto
{
a = {
b = {
c = null;
d = "hola";
};
};
e = {
f = required "error message...";
g = x: x;
h = null |> verify isString;
2026-01-28 18:04:58 +10:00
};
};
2026-01-28 23:54:53 +10:00
|> ...; # your logic here...
}
2026-01-28 18:04:58 +10:00
```