migrate to nt

This commit is contained in:
Emile Clark-Boman 2026-01-24 19:01:29 +10:00
parent 6c5e5fd880
commit 9ee4ae5f29
25 changed files with 8 additions and 2240 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
*.bak

View file

@ -1,10 +1,11 @@
{ {
description = "MyNib - My Nix Library"; description = "NixTypes (nt)";
inputs.systems.url = "github:nix-systems/default"; inputs = {
systems.url = "github:nix-systems/default";
mix.url = "github:emilelcb/mix";
};
outputs = {self, ...} @ inputs: let outputs = {...} @ inputs:
systems = import inputs.systems; import ./nt inputs;
in
import ./nib {inherit systems;};
} }

View file

@ -1,27 +0,0 @@
{systems, ...}: let
# TODO: move this to a new module
mkMod' = args: mod: import mod args;
mkMod = mkMod' {inherit systems nib;};
std = mkMod ./std;
panic = mkMod ./panic.nix;
parse = mkMod ./parse;
# patterns = mkMod ./patterns.nix;
types = mkMod ./types;
typesystem = mkMod ./typesystem.nix;
sys = mkMod ./sys.nix;
nib = std.mergeAttrsList [
# submodule content is accessible first by submodule name
# then by the name of the content (ie self.submodule.myFunc)
{inherit std types panic parse;}
# submodule content accessible directly (ie self.myFunc)
# patterns
typesystem
sys
];
in
nib

View file

@ -1,14 +0,0 @@
{nib, ...}: rec {
enfType = type: T:
assert (nib.isType type T
|| nib.panic.badType (nib.typeName type)); true;
enfSameType = T1: T2: enfType (nib.typeOf T1) T2;
enfAttrs = enfType (nib.typeOf {});
enfList = enfType (nib.typeOf []);
enfListOf = type: L:
assert (enfList L
&& builtins.all (T: nib.isType type T) L
|| nib.panic.badType "List ${nib.typeName type}" L); true;
}

View file

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

View file

@ -1,4 +0,0 @@
{nib, ...}: {
badType = expect: x:
throw "Expected type ${expect} but got ${nib.typeOf x}.";
}

View file

@ -1,7 +0,0 @@
{nib, ...} @ args: let
struct = import ./struct.nix args;
in
nib.std.mergeAttrsList [
# submodule is included directly to this module (ie self.myFunc)
struct
]

View file

@ -1,111 +0,0 @@
{nib, ...}: let
inherit
(nib.types)
Err
Ok'
firstErr
unwrapSome
isTerminal
unwrapTerminal
;
inherit
(nib.std)
attrValueAt
;
in rec {
cmpStructErr' = errBadKeys: errBadValues: path: S: T:
if builtins.isAttrs S && builtins.isAttrs T
then let
keysS = builtins.attrNames S;
keysT = builtins.attrNames T;
in
# ensure all key names match, then recurse
if !(keysS == keysT)
then errBadKeys path keysS keysT
else
(firstErr
(map
(k: cmpStructErr' errBadKeys errBadValues (path ++ [k]) (keysS.${k}) (keysT.${k}))
keysS))
else
# terminating leaf in recursion tree reached
# ensure values' types match
(builtins.typeOf S == builtins.typeOf T)
|| errBadValues path S T;
cmpStructErr = errBadKeys: errBadValues: cmpStructErr' errBadKeys errBadValues [];
cmpStruct =
cmpStructErr
(path: _: _:
Err {
reason = "keys";
inherit path;
})
(_: _: _: Ok');
cmpTypedStruct =
cmpStructErr
(path: _: _:
Err {
reason = "keys";
inherit path;
})
(path: _: _:
Err {
reason = "values";
inherit path;
});
cmpTypedPartialStruct =
cmpStructErr
(_: _: _: Ok')
(path: _: _:
Err {
reason = "values";
inherit path;
});
# Alternative to mapAttrsRecursiveCond
# Allows mapping directly from a child path
recmapCondFrom = path: cond: f: T: let
delegate = path': recmapCondFrom path' cond f;
in
if builtins.isAttrs T && cond path T
then builtins.mapAttrs (attr: leaf: delegate (path ++ [attr]) leaf) T
# else if builtins.isList T
# then map (leaf: delegate leaf)
else f path T;
recmapCond = recmapCondFrom [];
# Alternative to mapAttrsRecursive
# NOTE: refuses to go beyond Terminal types
recmap = recmapCond (_: leaf: !(isTerminal leaf));
overrideStructCond = cond: f: S: ext:
recmapCond
cond
(path: leaf:
attrValueAt path ext
|> unwrapSome (_: f leaf))
S;
# overrideStruct ensures no properties are evaluated (entirely lazy)
# TODO: should this be called "overlayStructs" or something? (its not exactly a override...)
# NOTE: respects Terminal types
overrideStruct =
overrideStructCond
(_: leaf: !(isTerminal leaf))
(leaf:
if isTerminal leaf
then unwrapTerminal leaf
else leaf);
# # overrideTypedPartialStruct must evaluate properties (not lazy)
# # for lazy evaluation use overrideStruct instead!
# overrideTypedPartialStruct = overrideStructs' cmpTypedPartialStruct;
overrideAttrs = A: B: A // B;
}

View file

@ -1,47 +0,0 @@
{nib, ...}: let
# Rust inspired pattern matching syntax:
# resultA = match [platform arch] [
# (Pattern ["darwin" Any] darwin_package)
# (Pattern ["openbsd" "x86_64"] openbsd_x86_64_package)
# (Pattern [(x: x == "linux") (y: y == "x86_64")] linux_x86_64_package)
# (Pattern (x: y: x == "linux" && y == "aarch64") linux_aarch64_package)
# (Pattern Any default_package)
# ];
# resultB = match [platform arch] [
# (["darwin" Any] |> case darwin_package)
# (["openbsd" "x86_64"] |> case openbsd_x86_64_package)
# ([(x: x == "linux") (y: y == "x86_64")] |> case linux_x86_64_package)
# ((x: y: x == "linux" && y == "aarch64") |> case linux_aarch64_package)
# (Any |> case default_package)
# ];
types = nib.types;
in rec {
Pattern = pattern: return: throw "not implemented";
case = return: pattern: Pattern pattern return;
matchesPattern' = pattern: subject: let
recurse = p: s:
nib.isSameType p s
&& (
if nib.isList p
then builtins.all (map (p: recurse p)) (nib.std.zipLists)
else if nib.isAttrs p
then builtins.all ()
else nib.eq p s
);
in
recurse pattern subject;
# maybe' :: TList a b -> TList [TPattern c d] -> TMaybe d
match' = subject: patterns:
nib.enfType (types.TList types.TPattern) patterns
&& builtins.foldl' (
fix: p:
if types.isNone fix
# maintain None as a fixed value
then fix
else matchesPattern' p
)
types.Some
patterns;
}

View file

@ -1,198 +0,0 @@
{nib, ...}: let
inherit
(builtins)
all
attrNames
elemAt
filter
getAttr
hasAttr
isAttrs
length
listToAttrs
mapAttrs
;
inherit
(nib.std)
foldl
;
inherit
(nib.types)
nullableToMaybe
;
in rec {
nameValuePair = name: value: {inherit name value;};
identityAttrs = value: {${value} = value;};
identityAttrsMany = values: map (v: identityAttrs v) values;
hasAttrs = list: xs: all (x: hasAttr x xs) list;
getAttrOr = default: name: xs:
if hasAttr name xs
then getAttr name xs
else default;
/**
Like `genAttrs`, but allows the name of each attribute to be specified in addition to the value.
The applied function should return both the new name and value as a `nameValuePair`.
::: {.warning}
In case of attribute name collision the first entry determines the value,
all subsequent conflicting entries for the same name are silently ignored.
:::
# Inputs
`xs`
: A list of strings `s` used as generator.
`f`
: A function, given a string `s` from the list `xs`, returns a new `nameValuePair`.
# Type
```
genAttrs' :: [ Any ] -> (Any -> { name :: String; value :: Any; }) -> AttrSet
```
# Examples
:::{.example}
## `lib.attrsets.genAttrs'` usage example
```nix
genAttrs' [ "foo" "bar" ] (s: nameValuePair ("x_" + s) ("y_" + s))
=> { x_foo = "y_foo"; x_bar = "y_bar"; }
```
:::
*/
genAttrs' = xs: f: listToAttrs (map f xs);
/**
Generate an attribute set by mapping a function over a list of
attribute names.
# Inputs
`names`
: Names of values in the resulting attribute set.
`f`
: A function, given the name of the attribute, returns the attribute's value.
# Type
```
genAttrs :: [ String ] -> (String -> Any) -> AttrSet
```
# Examples
:::{.example}
## `lib.attrsets.genAttrs` usage example
```nix
genAttrs [ "foo" "bar" ] (name: "x_" + name)
=> { foo = "x_foo"; bar = "x_bar"; }
```
:::
*/
genAttrs = names: f: genAttrs' names (n: nameValuePair n (f n));
mapAttrsRecursiveCond = cond: f: set: let
recurse = path:
mapAttrs (
name: value: let
next = path ++ [name];
in
if isAttrs value && cond value
then recurse next value
else f next value
);
in
recurse [] set;
mapAttrsRecursive = f: set: mapAttrsRecursiveCond (as: true) f set;
# form: attrValueAt :: list string -> set -> Maybe Any
# given path as a list of strings, return that value of an
# attribute set at that path
attrValueAt = path: xs:
foldl (left: right:
if isAttrs left && hasAttr right left
then left.${right}
else null)
xs
path
|> nullableToMaybe;
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);
/**
Filter an attribute set by removing all attributes for which the
given predicate return false.
# Inputs
`pred`
: Predicate taking an attribute name and an attribute value, which returns `true` to include the attribute, or `false` to exclude the attribute.
<!-- TIP -->
If possible, decide on `name` first and on `value` only if necessary.
This avoids evaluating the value if the name is already enough, making it possible, potentially, to have the argument reference the return value.
(Depending on context, that could still be considered a self reference by users; a common pattern in Nix.)
<!-- TIP -->
`filterAttrs` is occasionally the cause of infinite recursion in configuration systems that allow self-references.
To support the widest range of user-provided logic, perform the `filterAttrs` call as late as possible.
Typically that's right before using it in a derivation, as opposed to an implicit conversion whose result is accessible to the user's expressions.
`set`
: The attribute set to filter
# Type
```
filterAttrs :: (String -> Any -> Bool) -> AttrSet -> AttrSet
```
# Examples
:::{.example}
## `lib.attrsets.filterAttrs` usage example
```nix
filterAttrs (n: v: n == "foo") { foo = 1; bar = 2; }
=> { foo = 1; }
```
:::
*/
filterAttrs = pred: set: removeAttrs set (filter (name: !pred name set.${name}) (attrNames set));
}

View file

@ -1,14 +0,0 @@
{...} @ args: let
attrs = import ./attrs.nix args;
functions = import ./functions.nix args;
lists = import ./lists.nix args;
strings = import ./strings.nix args;
trivial = import ./trivial.nix args;
in
attrs.mergeAttrsList [
attrs
functions
lists
strings
trivial
]

View file

@ -1,9 +0,0 @@
{nib, ...}: let
inherit
(nib.std)
min
;
in {
# yeah fuck the waiter!! it was cold anyways :(
flipCurry = f: a: b: f b a;
}

View file

@ -1,85 +0,0 @@
{nib, ...}: let
inherit
(builtins)
concatMap
elemAt
foldl'
genList
length
;
inherit
(nib.std)
min
;
in rec {
foldl = op: nul: list: let
foldl' = n:
if n == -1
then nul
else op (foldl' (n - 1)) (elemAt list n);
in
foldl' (length list - 1);
crossLists = f: foldl (fs: args: concatMap (f: map f args) fs) [f];
findFirstIndex = pred: default: list: let
# A naive recursive implementation would be much simpler, but
# would also overflow the evaluator stack. We use `foldl'` as a workaround
# because it reuses the same stack space, evaluating the function for one
# element after another. We can't return early, so this means that we
# sacrifice early cutoff, but that appears to be an acceptable cost. A
# clever scheme with "exponential search" is possible, but appears over-
# engineered for now. See https://github.com/NixOS/nixpkgs/pull/235267
# Invariant:
# - if index < 0 then el == elemAt list (- index - 1) and all elements before el didn't satisfy pred
# - if index >= 0 then pred (elemAt list index) and all elements before (elemAt list index) didn't satisfy pred
#
# We start with index -1 and the 0'th element of the list, which satisfies the invariant
resultIndex =
foldl' (
index: el:
if index < 0
then
# No match yet before the current index, we need to check the element
if pred el
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 default
else resultIndex;
findFirst = pred: default: list: let
index = findFirstIndex pred null list;
in
if index == null
then default
else elemAt list index;
zipListsWith = f: fst: snd:
genList (n: f (elemAt fst n) (elemAt snd n)) (min (length fst) (length snd));
# zipLists = zipListsWith (fst: snd: {inherit fst snd;});
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 = sublist 0;
}

View file

@ -1,58 +0,0 @@
{nib, ...}: let
inherit
(builtins)
isPath
genList
replaceStrings
substring
stringLength
;
inherit
(nib.std)
warnIf
;
in rec {
# re-export builtin string methods
inherit
replaceStrings
substring
stringLength
;
escape = list: replaceStrings list (map (c: "\\${c}") list);
escapeRegex = escape (stringToCharacters "\\[{()^$?*+|.");
hasInfix = infix: content:
# Before 23.05, paths would be copied to the store before converting them
# to strings and comparing. This was surprising and confusing.
warnIf (isPath infix)
''
lib.strings.hasInfix: The first argument (${toString infix}) is a path value, but only strings are supported.
There is almost certainly a bug in the calling code, since this function always returns `false` in such a case.
This function also copies the path to the Nix store, which may not be what you want.
This behavior is deprecated and will throw an error in the future.''
(builtins.match ".*${escapeRegex infix}.*" "${content}" != null);
removeSuffix = suffix: str:
# Before 23.05, paths would be copied to the store before converting them
# to strings and comparing. This was surprising and confusing.
warnIf (isPath suffix)
''
lib.strings.removeSuffix: The first argument (${toString suffix}) is a path value, but only strings are supported.
There is almost certainly a bug in the calling code, since this function never removes any suffix in such a case.
This function also copies the path to the Nix store, which may not be what you want.
This behavior is deprecated and will throw an error in the future.''
(
let
sufLen = stringLength suffix;
sLen = stringLength str;
in
if sufLen <= sLen && suffix == substring (sLen - sufLen) sufLen str
then substring 0 (sLen - sufLen) str
else str
);
stringToCharacters = s: genList (p: substring p 1 s) (stringLength s);
}

View file

@ -1,23 +0,0 @@
{...}: let
inherit
(builtins)
warn
;
in {
id = x: x;
min = x: y:
if x < y
then x
else y;
max = x: y:
if x > y
then x
else y;
warnIf = cond: msg:
if cond
then warn msg
else x: x;
}

File diff suppressed because it is too large Load diff

View file

@ -1,74 +0,0 @@
# REF: https://github.com/numtide/flake-utils/blob/main/allSystems.nix
# XXX: TODO: Provide access to ALL of these systems
[
"aarch64-darwin"
"aarch64-genode"
"aarch64-linux"
"aarch64-netbsd"
"aarch64-none"
"aarch64_be-none"
"arm-none"
"armv5tel-linux"
"armv6l-linux"
"armv6l-netbsd"
"armv6l-none"
"armv7a-darwin"
"armv7a-linux"
"armv7a-netbsd"
"armv7l-linux"
"armv7l-netbsd"
"avr-none"
"i686-cygwin"
"i686-darwin"
"i686-freebsd13"
"i686-genode"
"i686-linux"
"i686-netbsd"
"i686-none"
"i686-openbsd"
"i686-windows"
"javascript-ghcjs"
"m68k-linux"
"m68k-netbsd"
"m68k-none"
"microblaze-linux"
"microblaze-none"
"microblazeel-linux"
"microblazeel-none"
"mips64el-linux"
"mipsel-linux"
"mipsel-netbsd"
"mmix-mmixware"
"msp430-none"
"or1k-none"
"powerpc-netbsd"
"powerpc-none"
"powerpc64-linux"
"powerpc64le-linux"
"powerpcle-none"
"riscv32-linux"
"riscv32-netbsd"
"riscv32-none"
"riscv64-linux"
"riscv64-netbsd"
"riscv64-none"
"rx-none"
"s390-linux"
"s390-none"
"s390x-linux"
"s390x-none"
"vc4-none"
"wasm32-wasi"
"wasm64-wasi"
"x86_64-cygwin"
"x86_64-darwin"
"x86_64-freebsd13"
"x86_64-genode"
"x86_64-linux"
"x86_64-netbsd"
"x86_64-none"
"x86_64-openbsd"
"x86_64-redox"
"x86_64-solaris"
"x86_64-windows"
]

View file

@ -1,42 +0,0 @@
{nib, ...}: let
crossLists = nib.std.crossLists;
identityAttrsMany = nib.std.identityAttrsMany;
# === Internal Helper Functions ===
toSystemName = arch: platform: "${arch}-${platform}";
listsToSystemNames = archs: platforms:
crossLists (arch: platform: toSystemName arch platform)
[
(builtins.attrValues archs)
(builtins.attrValues platforms)
];
in rec {
# REF: https://github.com/nix-systems/nix-systems
archs = identityAttrsMany [
"x86_64"
"aarch64"
"riscv64"
];
# REF: https://github.com/nix-systems/nix-systems
platforms = identityAttrsMany [
"linux"
"darwin"
];
# Nix System Identifier Lists - Default Supported Systems
# systems = systemsDefault;
systems.default = systems.x86_64 // systems.aarch64;
# Nix System Identifier Lists - All Potential Systems
systems.all = listsToSystemNames archs platforms;
# Nix System Identifier Lists - Platform Specific
systems.linux = listsToSystemNames archs [platforms.linux];
systems.darwin = listsToSystemNames archs [platforms.darwin];
# Nix System Identifier Lists - Architecture Specific
systems.x86_64 = listsToSystemNames [archs.x86_64] platforms;
systems.aarch64 = listsToSystemNames [archs.aarch64] platforms;
systems.riscv64 = listsToSystemNames [archs.riscv64] platforms;
}

View file

@ -1,37 +0,0 @@
{
systems,
nib,
...
}: let
std = nib.std;
in {
# === External Functions ===
withPkgs = repo: config: system:
import repo {
inherit system;
}
// config;
mkSys = input: let
# function taking a system as argument
pkgsFor = input.pkgs;
in {
inherit pkgsFor;
forAllSystems = f:
std.genAttrs systems (
system: f system (pkgsFor system)
);
};
mkUSys = input: let
# functions taking a system as argument
pkgsFor = input.pkgs;
upkgsFor = input.upkgs;
in {
inherit pkgsFor upkgsFor;
forAllSystems = f:
std.genAttrs systems (
system: f system (pkgsFor system) (upkgsFor system)
);
};
}

View file

@ -1,21 +0,0 @@
{nib, ...} @ args: let
fault = import ./fault.nix args;
maybe = import ./maybe.nix args;
res = import ./res.nix args;
terminal = import ./terminal.nix args;
in
nib.std.mergeAttrsList [
# submodule is included directly to this module (ie self.myFunc)
fault
maybe
res
terminal
rec {
# TODO
isAlgebraic = T: false;
isList = T: !isAlgebraic T && builtins.isList T;
isAttrs = T: !isAlgebraic T && builtins.isAttrs T;
}
]

View file

@ -1,18 +0,0 @@
{nib, ...}: rec {
# Fault Monad
# Wrapper around an error (ie builtins.abort)
Fault = error: {
_error_ = error;
};
# Pattern Matching
isFault = T: builtins.attrNames T == ["_error_"];
# Unwrap (Monadic Return Operation)
unwrapFault = T:
assert isFault T || nib.panic.badType "Fault" T;
T._error_;
# Map (Monadic Bind Operation)
mapFault = f: T: Fault (f (unwrapFault T));
}

View file

@ -1,110 +0,0 @@
{nib, ...}: let
findFirst = nib.std.findFirst;
# TODO: try get enum generation working (and other type constructors)
# Maybe = mkEnum "nib::Maybe" {
# Some = mkEnumVariant {value = "nix::String";};
# None = mkEnumVariant {};
# };
# TODO: you could even simplify this and pregenerate
# TODO: monadic operations by defining:
# mkMonad = mkEnum ... (blah blah blah)
#
# NOTE: internal view:
# NOTE: sum == enum
# NOTE: product == struct
# NOTE: terminal == literal/epsilon (in EBNF/Extended Backus-Naur form)
# Maybe = {
# # "adt" aka "algebraic data type"
# _adt_ = nib.types.adt.sum;
# _type_ = "Maybe"; # full type signature is "nib::Maybe"
# _vars_ = [
# {
# _adt_ = nib.types.adt.product;
# _type_ = "Some"; # type signature is "nib::Maybe::Some"
# _body_ = [
# # the name "value" was chosen by the user, its not inherit to nib
# value = {
# _adt_ = nib.types.adt.terminal;
# # nib.typeSig simply parses the string by splitting on "::" and forming a list
# _sig_ = nib.typeSig "nix::String";
# };
# ];
# };
# {
# _adt_ = nib.types.adt.terminal;
# _type_ = "None"; # type signature is "nib::Maybe::None"
# _sig_ = nib.typeSig "nix::Null";
# };
# ];
# None = {
#
# };
# _body_ = {
# _some_ = some; # allows _value_ to be null (yuck!!)
# _value_ = value;
# };
# };
#
# TODO: you could enforceType types as follows
# DEFINE: enforce = pred: var:
# DEFINE: assert (pred var) || throw "..."; var;
# DEFINE: enforceType = type:
# DEFINE: enforce (var: nib.typeOf var == type)
# value: let
# # NOTE: var is either a fixed-point of enforceType or fails
# # NOTE: either way you don't need to worry about a recursive definition!
# var = enforceType "nib::Maybe" var;
# in { ... }
in rec {
# Maybe (Option) Monad
Maybe = some: value: {
_some_ = some; # allows _value_ to be null (yuck!!)
_value_ = value;
};
Some = Maybe true;
None = Maybe false null;
# Pattern Matching (unsafe and safe variants)
isMaybe = T: builtins.attrNames T == ["_some_" "_value_"];
isSome' = T: isMaybe T && T._some_;
isSome = T:
assert isMaybe T || nib.panic.badType "Maybe" T;
isSome' T;
isNone' = T: isMaybe T && !T._some_;
isNone = T:
assert isMaybe T || nib.panic.badType "Maybe" T;
isNone' T;
# TODO: ensure you check isNone if isSome fails (otherwise it could be another type!)
# Unwrap (Monadic Return Operation)
unwrapMaybe = f: g: T:
if isSome T
then f T._value_
else g T._value_;
unwrapSome = unwrapMaybe (v: v);
unwrapNone = f: unwrapMaybe f (v: v);
# Map (Monadic Bind Operation)
mapMaybe = f: unwrapMaybe (v: Some (f v)) (_: None);
# NOTE: yes this does nothing, its only here so I don't forget
# NOTE: (ie when pregenerating monadic operations with a custom ADT module)
mapSome = mapMaybe;
# Conditionals
someOr = f: T:
if isSome T
then T
else f T;
noneOr = f: T:
if isNone T
then T
else f T;
firstSome = findFirst isSome' None;
nullableToMaybe = x:
if x == null
then None
else Some x;
}

View file

@ -1,51 +0,0 @@
{nib, ...}: let
findFirst = nib.std.findFirst;
in rec {
# Res (Result) Monad
Res = success: value: {
_success_ = success;
_value_ = value;
};
Ok = Res true;
Ok' = Ok "ok";
Err = Res false;
Err' = Err "err";
# Pattern Matching
isRes = R: builtins.attrNames R == ["_success_" "_value_"];
isOk' = R: isRes R && R._success_;
isOk = R:
assert isRes R || nib.panic.badType "Res" R;
isOk' R;
isErr' = R: isRes R && !R._success_;
isErr = R:
assert isRes R || nib.panic.badType "Res" R;
isErr' R;
# Unwrap (Monadic Return Operation)
unwrapRes = f: g: R:
if isOk R
then f R._value_
else g R._value_;
unwrapOk = unwrapRes (v: v);
unwrapErr = f: unwrapRes f (v: v);
# Map (Monadic Bind Operation)
mapRes = f: g: unwrapRes (v: Ok (f v)) (v: Err (f v));
mapOk = f: mapRes f (v: v);
mapErr = f: mapRes (v: v) f;
# Conditionals
okOr = f: R:
if isOk R
then R
else f R;
errOr = f: R:
if isErr R
then R
else f R;
# Standard Helpers
firstErr = findFirst isErr' Ok';
}

View file

@ -1,22 +0,0 @@
{nib, ...}: rec {
# Terminal Monad
# Wrapper around a value (preserves lazy eval for the value)
Terminal = value: {
_nbtype_ = "nib::Terminal";
_value_ = value;
};
# Pattern Matching
isTerminal = T:
builtins.isAttrs T
&& builtins.attrNames T == ["_nbtype_" "_value_"]
&& T._nbtype_ == "nib::Terminal";
# Unwrap (Monadic Return Operation)
unwrapTerminal = T:
assert isTerminal T || nib.panic.badType "Terminal" T;
T._value_;
# Map (Monadic Bind Operation)
mapTerminal = f: T: Terminal (f (unwrapTerminal T));
}

View file

@ -1,9 +0,0 @@
{...}: rec {
isType = type: T: type == typeOf T;
isSameType = T1: T2: typeOf T1 == typeOf T2;
# TODO
typeOf = builtins.typeOf;
# TODO
typeName = typeOf;
}