simplify 'struct'

This commit is contained in:
Joeri Exelmans 2025-06-02 15:28:07 +02:00
parent 43342e90d4
commit 366b1ec4e0
3 changed files with 57 additions and 52 deletions

View file

@ -1,33 +1,30 @@
import { unit } from "../primitives/unit.js"; import { unit } from "../primitives/unit.js";
import { capitalizeFirstLetter } from "../util/util.js"; import { capitalizeFirstLetter } from "../util/util.js";
import { newProduct, getLeft, getRight } from "./product.js";
export const makeConstructor = nParams => { export const makeConstructor = fieldNames => {
const internal = (nParams, ret) => { const internal = (fieldNames, ret) => {
if (nParams === 0) { if (fieldNames.length === 0) {
const result = ret(unit); const result = ret(unit);
return result; return result;
} }
return nextParam => { return nextParam => {
const wrappedName = 'wrapped_' + ret.name; const [fieldName, ...rest] = fieldNames;
const wrappedName = 'ctor_' + fieldName;
const newRet = { const newRet = {
[wrappedName]: inner => newProduct(nextParam)(ret(inner)), [wrappedName]: inner => ({[fieldName]: nextParam, ...ret(inner)}),
}[wrappedName]; }[wrappedName];
return internal(nParams-1, newRet); return internal(rest, newRet);
} }
}; };
const id = x => x; const id = x => x;
return internal(nParams, id); return internal(fieldNames, id);
}; };
export const makeGetters = fieldNames => { export const makeGetters = fieldNames => {
if (fieldNames.length === 0) { return fieldNames.map(fieldName => {
return [];
}
const [fieldName, ...rest] = fieldNames;
const getterName = `get${capitalizeFirstLetter(fieldName)}`; const getterName = `get${capitalizeFirstLetter(fieldName)}`;
return [ return {
{ [getterName]: obj => getLeft(obj) }[getterName], [getterName]: obj => obj[fieldName],
...makeGetters(rest).map(getter => ({[getter.name]: obj => getter(getRight(obj))}[getter.name])), }[getterName];
]; })
}; };

View file

@ -1,40 +1,14 @@
import { getDefaultTypeParser } from "../parser/type_parser.js"; import { getDefaultTypeParser } from "../parser/type_parser.js";
import { newDynamic } from "../primitives/dynamic.js"; import { newDynamic } from "../primitives/dynamic.js";
import { Type, Unit } from "../primitives/primitive_types.js";
import { zip } from "../util/util.js"; import { zip } from "../util/util.js";
import { map } from "./list.js"; import { map } from "./list.js";
import { getLeft, getRight } from "./product.js"; import { getLeft, getRight } from "./product.js";
import { makeConstructor, makeGetters } from "./struct.js"; import { makeConstructor, makeGetters } from "./struct.js";
import { fnType, prodType } from "./type_constructors.types.js"; import { fnType } from "./type_constructors.types.js";
// 'fields' is an array of (name: string, type: Type) pairs.
// e.g.:
// [{l: "x", r: Double}, {l: "y", r: Double}]
// results in the type (Double × (Double × Unit))
export const structType = (fields, rootSelf) => {
// console.log("structType..", fields);
if (fields.length === 0) {
return Unit;
}
const [field, ...rest] = fields;
const fieldType = getRight(field);
return prodType
(self => {
// console.log("requested left type (of structType)")
return fieldType(rootSelf || self);
})
(self => {
// console.log("requested right type (of structType)")
return structType(rest, self);
});
};
export const makeConstructorType = type => fields => { export const makeConstructorType = type => fields => {
if (fields.length === 0) { if (fields.length === 0) {
return type; return type;
// return structType(fields);
} }
const [field, ...rest] = fields; const [field, ...rest] = fields;
const fieldType = getRight(field); const fieldType = getRight(field);
@ -42,7 +16,6 @@ export const makeConstructorType = type => fields => {
}; };
export const makeGettersTypes = type => fields => { export const makeGettersTypes = type => fields => {
// const type = structType(fields);
return fields.map(field => { return fields.map(field => {
const fieldType = getRight(field); const fieldType = getRight(field);
return fnType(_ => type)(_ => fieldType); return fnType(_ => type)(_ => fieldType);
@ -51,14 +24,11 @@ export const makeGettersTypes = type => fields => {
export const makeModuleStruct = type => fields => { export const makeModuleStruct = type => fields => {
const fieldNames = map(fields)(getLeft); const fieldNames = map(fields)(getLeft);
// const type = structType(fields); const ctor = makeConstructor(fieldNames);
const ctor = makeConstructor(fields.length); const ctorType = makeConstructorType(type)(fields);
const ctorType = makeConstructorType(fields); const getterTypes = makeGettersTypes(type)(fields);
const getterTypes = makeGettersTypes(fields);
const getters = makeGetters(fieldNames); const getters = makeGetters(fieldNames);
const module = [ const module = [
// ["type", newDynamic(type)(Type)],
// constructor // constructor
["ctor", newDynamic(ctor)(ctorType)], ["ctor", newDynamic(ctor)(ctorType)],
@ -72,6 +42,6 @@ export const makeModuleStruct = type => fields => {
const mkType = getDefaultTypeParser(); const mkType = getDefaultTypeParser();
export const ModuleStruct = [ export const ModuleStruct = [
["structType" , newDynamic(structType )(mkType("[String*Type] -> Type" ))], // ["structType" , newDynamic(structType )(mkType("[String*Type] -> Type" ))],
["makeModuleStruct", newDynamic(makeModuleStruct)(mkType("[String*Type] -> [Dynamic]"))], ["makeModuleStruct", newDynamic(makeModuleStruct)(mkType("[String*Type] -> [Dynamic]"))],
]; ];

38
tests/struct.js Normal file
View file

@ -0,0 +1,38 @@
import assert from "node:assert";
import { makeTypeConstructor } from "../lib/meta/type_constructor.js";
import { Bool, Char, Int } from "../lib/primitives/primitive_types.js";
import { makeModuleStruct } from "../lib/structures/struct.types.js";
import { lsType } from "../lib/structures/type_constructors.types.js";
const symbolPerson = "Person__22a59ca589b4a7efdbe20b52f380e50f";
const Person = makeTypeConstructor(symbolPerson)(0);
const fields = [
{l: "name", r: lsType(_ => Char)},
{l: "age", r: Int},
{l: "isMale", r: Bool},
];
const [
[, {i: newPerson}],
[, {i: getName}],
[, {i: getAge}],
[, {i: getIsMale}],
] = makeModuleStruct(Person)(fields);
const expectedName = "billy";
const expectedAge = 99;
const expectedIsMale = true;
const billy = newPerson(expectedName)(expectedAge)(expectedIsMale);
const actualName = getName(billy);
const actualAge = getAge(billy);
const actualIsMale = getIsMale(billy);
assert.equal(actualName, expectedName);
assert.equal(actualAge, expectedAge);
assert.equal(actualIsMale, expectedIsMale);