From 471e1617e8cc0bc749712a7a6af2b2e9f988ffbf Mon Sep 17 00:00:00 2001 From: Emile Clark-Boman Date: Fri, 13 Feb 2026 01:43:41 +1000 Subject: [PATCH] add require type --- nt/precursor/bootstrap/naive/bootstrap.nix | 1 + nt/precursor/bootstrap/naive/require.nix | 44 ++++++++ nt/precursor/bootstrap/prim/list.nix | 15 ++- nt/precursor/nt/class.nix | 124 ++++++++++++++++++--- nt/precursor/nt/sig.nix | 6 +- 5 files changed, 169 insertions(+), 21 deletions(-) create mode 100644 nt/precursor/bootstrap/naive/require.nix diff --git a/nt/precursor/bootstrap/naive/bootstrap.nix b/nt/precursor/bootstrap/naive/bootstrap.nix index 7abafa9..4a3988d 100644 --- a/nt/precursor/bootstrap/naive/bootstrap.nix +++ b/nt/precursor/bootstrap/naive/bootstrap.nix @@ -4,6 +4,7 @@ bootstrap inputs [ { maybe = ./maybe.nix; + require = ./require.nix; terminal = ./terminal.nix; } ] diff --git a/nt/precursor/bootstrap/naive/require.nix b/nt/precursor/bootstrap/naive/require.nix new file mode 100644 index 0000000..07f4d0c --- /dev/null +++ b/nt/precursor/bootstrap/naive/require.nix @@ -0,0 +1,44 @@ +{...}: let + inherit + (builtins) + attrNames + concatStringsSep + isAttrs + typeOf + ; +in rec { + # Naive Require Type + Require = pred: let + got = typeOf pred; + in + assert (got == "lambda") + || throw '' + Naive type "Require" requires a predicate context of + primitive type "lambda"! But got "${got}". + ''; { + _pred = pred; + }; + + # Type Checking + isRequire = T: isAttrs T && attrNames T == ["_pred"]; + enfIsRequire = T: msg: let + throw' = got: throw "${msg}: expected naive type Require but got ${got}"; + attrs = + attrNames T + |> map (name: "\"${name}\"") + |> concatStringsSep ", "; + in + if isAttrs T + then isRequire T || throw' "attribute set with structure [${attrs}]" + else throw' "pred \"${toString T}\" of primitive type \"${typeOf T}\""; + + applyRequire = T: x: let + result = T._pred x; + got = typeOf result; + in + assert (got == "bool") + || throw '' + Naive type "Require" must return primitive type "bool"! + But got "${got}". + ''; result; +} diff --git a/nt/precursor/bootstrap/prim/list.nix b/nt/precursor/bootstrap/prim/list.nix index d006f13..b45e839 100644 --- a/nt/precursor/bootstrap/prim/list.nix +++ b/nt/precursor/bootstrap/prim/list.nix @@ -1,7 +1,6 @@ {...}: let inherit (builtins) - all elem elemAt foldl' @@ -9,7 +8,10 @@ length ; in rec { - contains = sub: list: all (x: elem x list) sub; + # contains = x: list: + # list + # |> foldl' (state: el: state || el == x) false; + contains = elem; sublist = start: count: list: let len = length list; @@ -82,4 +84,13 @@ in rec { if index == null then default else elemAt list index; + + unique = list: + list + |> foldl' ( + acc: el: + if acc |> contains el + then acc + else acc ++ [el] + ) []; } diff --git a/nt/precursor/nt/class.nix b/nt/precursor/nt/class.nix index 7a9190b..0623efd 100644 --- a/nt/precursor/nt/class.nix +++ b/nt/precursor/nt/class.nix @@ -1,18 +1,24 @@ {this, ...}: let inherit (builtins) + all + attrValues + concatLists + filter isFunction length - mapAttrs + mergeAttrsList partition + typeOf ; inherit (this) enfIsType enfIsClassSig + isClass + enfIsTypeSig ntTrapdoorKey - parseClassSig typeSig ; @@ -28,6 +34,7 @@ projectOnto recdef removeAttrsRec + unique ; inherit @@ -35,11 +42,19 @@ Terminal ; + inherit + (this.naive.require) + isRequire + ; + classDecl = { derive = Terminal []; ops = Terminal {}; }; + # XXX: i think this works? + typeDecl = classDecl; + unwrapBuilder = builder: Self: if isFunction builder then builder Self @@ -56,33 +71,87 @@ # 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 + parseOps = decl: let + opsFormatted = assert ( + decl.ops + |> attrValues + |> all isFunction + ) + || throw '' + Typeclass opts must be specified as an attrset of functions. + Either clarify the deriving class by partial name (ie `MyClass.myOp = ...`) + or by complete type signature (ie `$${typeSig MyClass}.myOp = ...`). + ''; + decl.ops + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + # XXX: WARNING: TODO: this code is unfinished!!!! + |> partition (x: true); # i forgor, TODO: rember :( + + # NOTE: reqDerived can/will contain duplicates + # NOTE: it's wasteful to filter uniques now, just wait + reqDerived = + decl.derive + |> map (x: x.req) + |> mergeAttrsList; + + reqSelf = + decl.ops + |> filter isRequire; + reqPaths = - req - |> mapAttrs (name: let - segs = parseClassSig name; - in - value: segs ++ [value]); + (reqDerived ++ reqSelf) + |> unique # XXX: now we filter uniques + |> 3; + + # reqPaths = + # decl.req + # |> mapAttrs (name: let + # segs = parseClassSig name; + # in + # value: segs ++ [value]); # XXX: TODO: having to specify the full namespace sucks :( - matches = partition (flip hasAttrAt ops) reqPaths; + matches = partition (flip hasAttrAt decl.ops) reqPaths; pathsMissing = matches.wrong; - opsSelf = removeAttrsRec matches.right ops; - opsDerived = removeAttrsRec matches.wrong ops; + opsSelf = removeAttrsRec matches.right decl.ops; + opsDerived = removeAttrsRec matches.wrong decl.ops; in { inherit opsSelf opsDerived pathsMissing; success = length pathsMissing == 0; }; mkClass = sig: decl: - assert enfIsClassSig sig "mkClass"; let + assert enfIsClassSig sig "mkClass"; + assert decl.derive + |> all (x: + isClass x + || throw '' + NixTypes can only derive from NixType classes! + However, ${sig} derives from invalid ${x} (type: ${typeOf x}). + ''); let + # XXX: TODO: enforce that every derive is a class! allDerivedClasses = decl.derive - |> map (class: typeSig class ++ class.${ntTrapdoorKey}.derive); + |> map (class: [typeSig class] ++ class.${ntTrapdoorKey}.derive) + |> concatLists; - parseResult = parseOps decl.ops decl.req; + parseResult = parseOps decl; inherit (parseResult) opsSelf @@ -98,15 +167,38 @@ ${ntTrapdoorKey} = { inherit sig; derive = allDerivedClasses; - ops = {${sig} = opsSelf;} // opsDerived; - req = null; # XXX: TODO make it more advanced + ops = opsDerived // {${sig} = opsSelf;}; + req = null; }; }; }; + + mkType = sig: decl: + assert enfIsTypeSig sig "mkType"; let + allDerivedClasses = + decl.derive + |> map (class: typeSig class ++ class.${ntTrapdoorKey}.derive); + + parseResult = parseOps decl; + inherit + (parseResult) + opsSelf + opsDerived + ; + in + # XXX: WARNING: classes currently *shouldn't* be able to inherit ops (i think?) + assert parseResult.success || throw "TODO"; + opsSelf.mk; in { Class = sig: builder: recdef (Self: unwrapBuilder builder Self |> parseDecl classDecl |> mkClass sig); + + Type = sig: builder: + recdef (Self: + unwrapBuilder builder Self + |> parseDecl typeDecl + |> mkType sig); } diff --git a/nt/precursor/nt/sig.nix b/nt/precursor/nt/sig.nix index 0ecf164..4da1c1d 100644 --- a/nt/precursor/nt/sig.nix +++ b/nt/precursor/nt/sig.nix @@ -28,6 +28,7 @@ (this.naive.maybe) bindMaybe isSome + Some ; in rec { parseSig = sig: let @@ -69,12 +70,11 @@ in rec { typeSig = T: assert enfIsNT T "nt.typeSig"; openNT T - |> bindMaybe (getAttr "sig") - |> isSome; + |> bindMaybe (getAttr "sig"); toTypeSig = x: if isString x - then x + then Some x else typeSig x; # NOTE: we're testing how similar `list` is to `toTypeSig type` (non-commutative)