From 160b59b8bf97b634346903c91bdf68d798b66aa6 Mon Sep 17 00:00:00 2001 From: imxyy_soope_ Date: Sun, 11 Jan 2026 14:53:05 +0800 Subject: [PATCH] feat: __functor --- nix-js/runtime-ts/src/helpers.ts | 20 +++++++++++++++++++- nix-js/runtime-ts/src/index.ts | 11 ++++++++++- nix-js/runtime-ts/src/type-assert.ts | 13 +++++++++++-- nix-js/src/codegen.rs | 16 ++++++++++------ 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/nix-js/runtime-ts/src/helpers.ts b/nix-js/runtime-ts/src/helpers.ts index 83de5cb..a8b5070 100644 --- a/nix-js/runtime-ts/src/helpers.ts +++ b/nix-js/runtime-ts/src/helpers.ts @@ -3,10 +3,11 @@ */ import type { NixValue, NixAttrs, NixBool, NixString } from "./types"; -import { forceAttrs, forceString } from "./type-assert"; +import { forceAttrs, forceFunction, forceString, typeName } from "./type-assert"; import { isAttrs } from "./builtins/type-check"; import { coerceToString, StringCoercionMode } from "./builtins/conversion"; import { type NixStringContext, mkStringWithContext } from "./string-context"; +import { force } from "./thunk"; /** * Concatenate multiple values into a string with context @@ -144,3 +145,20 @@ export const validateParams = ( return forced_arg; }; + +export const call = (func: NixValue, arg: NixValue): NixValue => { + const forcedFunc = force(func); + if (typeof forcedFunc === "function") { + return forcedFunc(arg); + } + if ( + typeof forcedFunc === "object" && + !Array.isArray(forcedFunc) && + forcedFunc !== null && + "__functor" in forcedFunc + ) { + const functor = forceFunction(forcedFunc.__functor); + return forceFunction(functor(forcedFunc))(arg); + } + throw new Error(`attempt to call something which is not a function but ${typeName(forcedFunc)}`); +}; diff --git a/nix-js/runtime-ts/src/index.ts b/nix-js/runtime-ts/src/index.ts index 85d20f1..3e76519 100644 --- a/nix-js/runtime-ts/src/index.ts +++ b/nix-js/runtime-ts/src/index.ts @@ -5,7 +5,15 @@ */ import { createThunk, force, isThunk, IS_THUNK } from "./thunk"; -import { select, selectWithDefault, validateParams, resolvePath, hasAttr, concatStringsWithContext } from "./helpers"; +import { + select, + selectWithDefault, + validateParams, + resolvePath, + hasAttr, + concatStringsWithContext, + call, +} from "./helpers"; import { op } from "./operators"; import { builtins, PRIMOP_METADATA } from "./builtins"; import { coerceToString, StringCoercionMode } from "./builtins/conversion"; @@ -23,6 +31,7 @@ export const Nix = { IS_THUNK, HAS_CONTEXT, + call, hasAttr, select, selectWithDefault, diff --git a/nix-js/runtime-ts/src/type-assert.ts b/nix-js/runtime-ts/src/type-assert.ts index 5dbc4a6..4708fe5 100644 --- a/nix-js/runtime-ts/src/type-assert.ts +++ b/nix-js/runtime-ts/src/type-assert.ts @@ -3,12 +3,21 @@ * These functions force evaluation and verify the type, throwing errors on mismatch */ -import type { NixValue, NixList, NixAttrs, NixFunction, NixInt, NixFloat, NixNumber, NixString } from "./types"; +import type { + NixValue, + NixList, + NixAttrs, + NixFunction, + NixInt, + NixFloat, + NixNumber, + NixString, +} from "./types"; import { isStringWithContext } from "./types"; import { force } from "./thunk"; import { getStringValue } from "./string-context"; -const typeName = (value: NixValue): string => { +export const typeName = (value: NixValue): string => { const val = force(value); if (typeof val === "bigint") return "int"; diff --git a/nix-js/src/codegen.rs b/nix-js/src/codegen.rs index 341ba60..ddbeaaa 100644 --- a/nix-js/src/codegen.rs +++ b/nix-js/src/codegen.rs @@ -49,17 +49,13 @@ impl Compile for Ir { Ir::Func(x) => x.compile(ctx), Ir::AttrSet(x) => x.compile(ctx), Ir::List(x) => x.compile(ctx), - &Ir::Call(Call { func, arg }) => { - let func = ctx.get_ir(func).compile(ctx); - let arg = ctx.get_ir(arg).compile(ctx); - format!("Nix.force({func})({arg})") - } + Ir::Call(x) => x.compile(ctx), Ir::Arg(x) => format!("arg{}", x.0), Ir::Let(x) => x.compile(ctx), Ir::Select(x) => x.compile(ctx), &Ir::Thunk(expr_id) => { let inner = ctx.get_ir(expr_id).compile(ctx); - format!("Nix.createThunk(()=>({}))", inner) + format!("Nix.createThunk(()=>({}),\"expr{}\")", inner, expr_id.0) } &Ir::ExprRef(expr_id) => { format!("expr{}", expr_id.0) @@ -171,6 +167,14 @@ impl Func { } } +impl Compile for Call { + fn compile(&self, ctx: &Ctx) -> String { + let func = ctx.get_ir(self.func).compile(ctx); + let arg = ctx.get_ir(self.arg).compile(ctx); + format!("Nix.call({func}, {arg})") + } +} + impl Compile for Let { fn compile(&self, ctx: &Ctx) -> String { let info = &self.binding_sccs;