import { SymbolT, Type } from "../primitives/types.js"; import { makeTypeConstructor } from "../type_constructor.js"; import { Module, String } from "./list.js"; import { prodType, fnType, lsType } from "./types.js"; function capitalizeFirstLetter(val) { return String(val).charAt(0).toUpperCase() + String(val).slice(1); } export const createStruct = (typeVars, symbol, fields) => { const makeConstructor = (remainingFields, obj={}) => { if (remainingFields.length===0) { return obj; } const {left: fieldName} = remainingFields[remainingFields.length-1]; return v => makeConstructor( remainingFields.slice(0,-1), Object.assign({[fieldName]: v}, obj)); }; const constructor = makeConstructor(fields); const type = makeTypeConstructor(symbol)(typeVars.size); const types = [ type ]; const recordFnType = inType => outType => { const fnT = fnType(inType)(outType); types.push(fnT); return fnT; } const makeConstructorType = (remainingFields, type) => { if (remainingFields.length===0) { return type; } const {right: fieldType} = remainingFields[remainingFields.length-1]; return recordFnType(makeConstructorType(remainingFields.slice(0,-1)))(fieldType); }; const constructorType = makeConstructorType(fields); const functions = [ ["constructor", constructor, constructorType], ...fields.map(({left: fieldName, right: fieldType}) => { const getterName = 'get'+capitalizeFirstLetter(fieldName); const getter = { // stupid trick to give the JS-function a computed name. // only important for debugging, so it says [Function: getAge] instead of [Function (anonymous)]: [getterName]: obj => obj[fieldName], }[getterName]; if (typeVars.has(fieldType)) { // getterFnType = recordFnType(type)(fieldType) } const getterFnType = recordFnType(type)(fieldType); return [fieldName, getter, getterFnType]; }), ]; const module = {l:[ {i: type, t: Type}, ...functions.flatMap(([_, getter, getterFnType]) => [ {i: getter , t: getterFnType}, ]), ...types.map(type => ({i: type, t: Type})), ]}; return { module, constructor, functions: Object.fromEntries(functions), }; }; export const createNominalADTModuleFnType = fnType(SymbolT) (fnType(lsType(prodType(String)(Type))) (Module));