From ea01dcc8275889e10818c6c07aabd6ee9f115cee Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Sun, 14 Dec 2025 22:41:17 +1000 Subject: [PATCH] add panic.nix and assertions to type checks --- nib/default.nix | 3 ++- nib/panic.nix | 4 ++++ nib/types/fault.nix | 10 ++++++---- nib/types/maybe.nix | 26 ++++++++++++++++---------- nib/types/res.nix | 26 ++++++++++++++++---------- 5 files changed, 44 insertions(+), 25 deletions(-) create mode 100644 nib/panic.nix diff --git a/nib/default.nix b/nib/default.nix index 515f205..8a0df53 100644 --- a/nib/default.nix +++ b/nib/default.nix @@ -6,11 +6,12 @@ std = mkMod ./std; types = mkMod ./types; parse = mkMod ./parse; + panic = mkMod ./panic.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 parse;} + {inherit std types panic parse;} # submodule is included directly to this module (ie self.myFunc) diff --git a/nib/panic.nix b/nib/panic.nix new file mode 100644 index 0000000..eb73ca8 --- /dev/null +++ b/nib/panic.nix @@ -0,0 +1,4 @@ +{...}: { + badType = expect: x: + throw "Expected type ${expect} but got ${builtins.typeOf x}."; +} diff --git a/nib/types/fault.nix b/nib/types/fault.nix index 76fccc4..8abb52f 100644 --- a/nib/types/fault.nix +++ b/nib/types/fault.nix @@ -1,4 +1,4 @@ -{...}: rec { +{nib, ...}: rec { # Fault Monad # Wrapper around an error (ie builtins.abort) Fault = error: { @@ -6,11 +6,13 @@ }; # Pattern Matching - isFault = F: builtins.attrNames F == ["_error_"]; + isFault = T: builtins.attrNames T == ["_error_"]; # Unwrap (Monadic Return Operation) - unwrapFault = F: F._error_; + unwrapFault = T: + assert isFault T || nib.panic.badType "Fault" T; + T._error_; # Map (Monadic Bind Operation) - mapFault = f: F: Fault (f (unwrapFault F)); + mapFault = f: T: Fault (f (unwrapFault T)); } diff --git a/nib/types/maybe.nix b/nib/types/maybe.nix index 800cde8..3bb41b4 100644 --- a/nib/types/maybe.nix +++ b/nib/types/maybe.nix @@ -62,28 +62,34 @@ in rec { _some_ = some; # allows _value_ to be null (yuck!!) _value_ = value; }; - Some = value: Res true value; + Some = Res true; None = Maybe false null; - # Pattern Matching + # Pattern Matching (unsafe and safe variants) isMaybe = T: builtins.attrNames T == ["_some_" "_value_"]; - isSome = T: isMaybe T && T._some_; - isNone = T: isMaybe T && !T._some_; + 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: T: - if isSome T - then Some (f T._value_) - else None; + 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 = f: mapMaybe f; + mapSome = mapMaybe; # Conditionals someOr = f: T: @@ -96,7 +102,7 @@ in rec { then T else f T; - firstSome = findFirst isSome None; + firstSome = findFirst isSome' None; nullableToMaybe = x: if x == null diff --git a/nib/types/res.nix b/nib/types/res.nix index 23b7484..42e9afd 100644 --- a/nib/types/res.nix +++ b/nib/types/res.nix @@ -6,28 +6,34 @@ in rec { _success_ = success; _value_ = value; }; - Ok = value: Res true value; + Ok = Res true; Ok' = Ok "ok"; - Err = value: Res false value; + Err = Res false; Err' = Err "err"; # Pattern Matching isRes = R: builtins.attrNames R == ["_success_" "_value_"]; - isOk = R: isRes R && R._success_; - isErr = R: isRes R && !R._success_; + 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 = f: unwrapRes (R: R._value_) f; - unwrapErr = f: unwrapRes f (R: R._value_); + unwrapOk = unwrapRes (v: v); + unwrapErr = f: unwrapRes f (v: v); # Map (Monadic Bind Operation) - mapRes = f: g: unwrapRes (R: Ok (f R)) (R: Err (f R)); - mapOk = f: mapRes f (x: x); - mapErr = f: mapRes (x: x) f; + 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: @@ -41,5 +47,5 @@ in rec { else f R; # Standard Helpers - firstErr = findFirst isErr Ok'; + firstErr = findFirst isErr' Ok'; }