fix(builtins.zipAttrsWith): laziness

This commit is contained in:
2026-01-24 16:44:28 +08:00
parent 10430e2006
commit 296c0398a4
3 changed files with 35 additions and 36 deletions

View File

@@ -107,3 +107,36 @@ export const groupBy =
} }
return attrs; return attrs;
}; };
export const zipAttrsWith =
(f: NixValue) =>
(list: NixValue): NixValue => {
const listForced = forceList(list);
// Map to collect all values for each attribute name
const attrMap = new Map<string, NixValue[]>();
// Iterate through each attribute set in the list
for (const item of listForced) {
const attrs = forceAttrs(item);
// Collect all attribute names and their values
for (const [key, value] of Object.entries(attrs)) {
if (!attrMap.has(key)) {
attrMap.set(key, []);
}
attrMap.get(key)!.push(value);
}
}
// Build the result attribute set
const result: Record<string, NixValue> = {};
for (const [name, values] of attrMap.entries()) {
// Apply f to name and values list
// f is curried: f name values
result[name] = createThunk(() => forceFunction(forceFunction(f)(name))(values));
}
return result;
};

View File

@@ -172,6 +172,7 @@ export const builtins: any = {
intersectAttrs: mkPrimop(attrs.intersectAttrs, "intersectAttrs", 2), intersectAttrs: mkPrimop(attrs.intersectAttrs, "intersectAttrs", 2),
catAttrs: mkPrimop(attrs.catAttrs, "catAttrs", 2), catAttrs: mkPrimop(attrs.catAttrs, "catAttrs", 2),
groupBy: mkPrimop(attrs.groupBy, "groupBy", 2), groupBy: mkPrimop(attrs.groupBy, "groupBy", 2),
zipAttrsWith: mkPrimop(attrs.zipAttrsWith, "zipAttrsWith", 2),
stringLength: mkPrimop(string.stringLength, "stringLength", 1), stringLength: mkPrimop(string.stringLength, "stringLength", 1),
substring: mkPrimop(string.substring, "substring", 3), substring: mkPrimop(string.substring, "substring", 3),
@@ -247,7 +248,6 @@ export const builtins: any = {
splitVersion: mkPrimop(misc.splitVersion, "splitVersion", 1), splitVersion: mkPrimop(misc.splitVersion, "splitVersion", 1),
traceVerbose: mkPrimop(misc.traceVerbose, "traceVerbose", 2), traceVerbose: mkPrimop(misc.traceVerbose, "traceVerbose", 2),
tryEval: mkPrimop(misc.tryEval, "tryEval", 1), tryEval: mkPrimop(misc.tryEval, "tryEval", 1),
zipAttrsWith: mkPrimop(misc.zipAttrsWith, "zipAttrsWith", 2),
builtins: createThunk(() => builtins, "builtins"), builtins: createThunk(() => builtins, "builtins"),
currentSystem: createThunk(() => { currentSystem: createThunk(() => {

View File

@@ -2,7 +2,7 @@
* Miscellaneous builtin functions * Miscellaneous builtin functions
*/ */
import { force } from "../thunk"; import { createThunk, force } from "../thunk";
import { CatchableError } from "../types"; import { CatchableError } from "../types";
import type { NixAttrs, NixBool, NixStrictValue, NixValue } from "../types"; import type { NixAttrs, NixBool, NixStrictValue, NixValue } from "../types";
import { forceList, forceAttrs, forceFunction, forceStringValue, forceString } from "../type-assert"; import { forceList, forceAttrs, forceFunction, forceStringValue, forceString } from "../type-assert";
@@ -355,37 +355,3 @@ export const tryEval = (e: NixValue): { success: NixBool; value: NixStrictValue
} }
} }
}; };
export const zipAttrsWith =
(f: NixValue) =>
(list: NixValue): NixValue => {
const listForced = forceList(list);
// Map to collect all values for each attribute name
const attrMap = new Map<string, NixValue[]>();
// Iterate through each attribute set in the list
for (const item of listForced) {
const attrs = forceAttrs(force(item) as NixValue);
// Collect all attribute names and their values
for (const [key, value] of Object.entries(attrs)) {
if (!attrMap.has(key)) {
attrMap.set(key, []);
}
attrMap.get(key)!.push(value);
}
}
// Build the result attribute set
const result: Record<string, NixValue> = {};
for (const [name, values] of attrMap.entries()) {
// Apply f to name and values list
// f is curried: f name values
const fWithName = forceFunction(f)(name);
result[name] = forceFunction(fWithName)(values);
}
return result;
};