pretty print enhancements + comparison of generic functions

This commit is contained in:
Joeri Exelmans 2025-05-08 22:59:01 +02:00
parent bbac7858ae
commit 9e1f679dba
15 changed files with 93 additions and 45 deletions

View file

@ -1,6 +1,8 @@
import { getInst, getType } from "../primitives/dynamic.js";
import { SymbolBool, SymbolBottom, SymbolByte, SymbolChar, SymbolDouble, SymbolDynamic, SymbolInt, SymbolUUID, SymbolType, SymbolUnit } from "../primitives/primitive_types.js";
import { UNBOUND_SYMBOLS } from "../primitives/typevars.js";
import { symbolDict, symbolFunction, symbolList, symbolProduct, symbolSet, symbolSum } from "../structures/type_constructors.js";
import { prettyT } from "../util/pretty.js";
import { compareBools, compareNumbers, compareStrings, compareSymbols, compareUnits } from "./primitives.js";
import { compareDicts, compareFunctions, compareLists, compareProducts, compareSets, compareSums } from "./structures.js";
import { compareTypes } from "./type.js";
@ -9,6 +11,8 @@ export const compareDynamic = x => y =>
compareTypes(getType(x))(getType(y))
|| makeCompareFn(getType(x))(getInst(x))(getInst(y));
const cannotCompareTypeVarInstances = _ => _ => { throw new Error("Cannot compare instance of type variables"); }
const typeSymbolToCmp = new Map([
[SymbolInt , compareNumbers],
[SymbolBool , compareBools],
@ -29,11 +33,20 @@ const typeSymbolToCmp = new Map([
[symbolList , compareLists],
[symbolSet , compareSets],
[symbolDict , compareDicts],
// even though we cannot compare typevar instances,
// we still need to give a function or shit will break
// types that contain typevars are never instantiated,
// EXCEPT:
// - generic functions (but for function comparison we don't look at the in/out types)
// - empty list, empty set, empty dict are all generic, but for each type there is only one empty element, so it never needs to be compared to another one
...UNBOUND_SYMBOLS.map(uuid => [uuid, cannotCompareTypeVarInstances]),
]);
export const makeCompareFn = type => {
return type.params.reduce(
(acc, cur) => acc(makeCompareFn(cur(type))),
typeSymbolToCmp.get(type.symbol)
|| (() => { throw new Error(`don't know how to compare instances of '${prettyT(type)}'`); })()
);
};

View file

@ -1,8 +1,13 @@
import { getHumanReadableName } from "../primitives/symbol.js";
import { inspect } from "util";
import { prettyT } from "../util/pretty.js";
const __makeTypeConstructor = (symbol, nAry, params) => {
if (nAry === 0) {
return { symbol, params };
return {
symbol, params,
[inspect.custom](depth, options, inspect){ return options.stylize(prettyT(this), 'null'); },
};
}
// only for debugging, do we give the function a name
const fName = `${getHumanReadableName(symbol).toLowerCase()}Type${params.length>0?params.length:''}`;

View file

@ -5,8 +5,8 @@ import { Dynamic } from "../primitives/primitive_types.js";
import { getHumanReadableName } from "../primitives/symbol.js";
import { getSymbol } from "../primitives/type.js";
import { TYPE_VARS } from "../primitives/typevars.js";
import { dictType, fnType, lsType, prodType, sumType } from "../structures/type_constructors.js";
import { setType } from "../structures/type_constructors.js";
import { dictType, fnType, lsType, prodType, sumType } from "../structures/type_constructors.types.js";
import { setType } from "../structures/type_constructors.types.js";
// A very stupid little parser, that can only parse:
// - primitives => simply maps onto types.

View file

@ -1,6 +1,7 @@
import { assignFn } from "../generics/generics.js";
export const newDynamic = i => t => ({i, t});
export const getInst = lnk => lnk.i;
export const getType = lnk => lnk.t;

View file

@ -1,3 +1,7 @@
export const unit = {};
import { inspect } from "node:util";
export const unit = {
[inspect.custom](depth, options, inspect){ return '()'; }
};
export const eqUnit = _ => _ => true;

View file

@ -1,12 +1,21 @@
import { RBTreeWrapper } from "../util/rbtree_wrapper.js";
import { indent } from "../util/util.js";
import { newProduct } from "./product.js";
import { newLeft, newRight } from "./sum.js";
export const emptyDict = compareFn => RBTreeWrapper.new((x, y) => compareFn(x)(y));
// only for debugging
function inspectDict(_depth, options, inspect) {
const entries = [];
this.tree.forEach((key, val) => { entries.push(`${inspect(key, options)} => ${inspect(val, options)}`); });
return `{\n${indent(entries.join(',\n'),2)}\n}`;
}
export const has = dict => key => dict.tree.get(key) === true;
export const set = dict => key => value => new RBTreeWrapper(dict.tree.remove(key).insert(key, value));
export const remove = dict => key => new RBTreeWrapper(dict.tree.remove(key));
export const emptyDict = compareFn => RBTreeWrapper.new((x, y) => compareFn(x)(y), inspectDict);
export const has = dict => key => dict.tree.get(key) !== undefined;
export const get = dict => key => dict.tree.get(key);
export const set = dict => key => value => new RBTreeWrapper(dict.tree.remove(key).insert(key, value), inspectDict);
export const remove = dict => key => new RBTreeWrapper(dict.tree.remove(key), inspectDict);
export const length = dict => dict.tree.length;
export const first = dict => dict.tree.begin;

View file

@ -2,9 +2,12 @@ import { makeTypeParser } from "../parser/type_parser.js";
import { makeTypeConstructor } from "../meta/type_constructor.js";
import { emptyDict, first, has, last, length, read, remove, set } from "./dict.js";
const dictIterator = makeTypeConstructor('DictIterator__d9d175b6bfd1283f00851a99787d0499')(2);
export const symbolDictIterator = 'DictIterator__d9d175b6bfd1283f00851a99787d0499';
const dictIterator = makeTypeConstructor(symbolDictIterator)(2);
const mkType = makeTypeParser({
// we invent a bit of syntax for the dict iterator type, which takes two type parameters (key and value)
extraInfixOperators: [['|=>|', dictIterator]],
});

View file

@ -2,13 +2,21 @@ import { newRight } from "./sum.js";
import { newProduct } from "./product.js";
import { unit } from "../primitives/unit.js";
import { RBTreeWrapper } from "../util/rbtree_wrapper.js";
import { indent } from "../util/util.js";
// only for debugging
function inspectSet(_depth, options, inspect) {
const keys = [];
this.tree.forEach((key) => { keys.push(inspect(key, options)); });
return `{\n${indent(keys.join(',\n'), 2)}\n}`;
}
// (a -> a -> Int) -> Set(a)
export const emptySet = compareFn => RBTreeWrapper.new((x, y) => compareFn(x)(y));
export const emptySet = compareFn => RBTreeWrapper.new((x, y) => compareFn(x)(y), inspectSet);
export const has = set => key => set.tree.get(key) === true;
export const add = set => key => set.tree.get(key) === true ? set : new RBTreeWrapper(set.tree.insert(key, true));
export const remove = set => key => new RBTreeWrapper(set.tree.remove(key));
export const add = set => key => set.tree.get(key) === true ? set : new RBTreeWrapper(set.tree.insert(key, true), inspectSet);
export const remove = set => key => new RBTreeWrapper(set.tree.remove(key), inspectSet);
export const length = set => set.tree.length;
export const fold = set => callback => initial => {

View file

@ -2,7 +2,9 @@ import { makeTypeParser } from "../parser/type_parser.js";
import { makeTypeConstructor } from "../meta/type_constructor.js";
import { emptySet, has, add, remove, length, first, read, last, fold } from "./set.js";
const setIterator = makeTypeConstructor('SetIterator__f6b0ddd78ed41c58e5a442f2681da011')(1);
export const symbolSetIterator = 'SetIterator__f6b0ddd78ed41c58e5a442f2681da011';
const setIterator = makeTypeConstructor(symbolSetIterator)(1);
const mkType = makeTypeParser({
extraBracketOperators: [['<', ['>', setIterator]]],

View file

@ -1,17 +1,8 @@
// to break up dependency cycles, type constructors are defined in their own JS module
import { makeTypeConstructor } from "../meta/type_constructor.js";
export const symbolFunction = "Function__c2433e31fa574a2cb3b6b5d62ac9d4b2";
export const symbolSum = "Sum__89b731efa6344ea0b6a8663a45cf3ea8";
export const symbolProduct = "Product__89351ecdedfb4b05b2a5a6cc0c383e12";
export const symbolList = "List__daa8de8a9047435e96034ec64f2da3a1";
export const symbolSet = "Set__8fef2c1873df4327ac31bd61d2ecf7e0";
export const symbolDict = "Dict__d7158547322549ac9f7f8176aec123dd";
export const fnType = makeTypeConstructor(symbolFunction)(2);
export const sumType = makeTypeConstructor(symbolSum)(2);
export const prodType = makeTypeConstructor(symbolProduct)(2);
export const lsType = makeTypeConstructor(symbolList)(1);
export const setType = makeTypeConstructor(symbolSet)(1);
export const dictType = makeTypeConstructor(symbolDict)(2);

View file

@ -1,8 +1,13 @@
import { getDefaultTypeParser } from "../parser/type_parser.js";
import { UUID } from "../primitives/primitive_types.js";
import { dictType, fnType, lsType, prodType, setType, sumType, symbolDict, symbolFunction, symbolList, symbolProduct, symbolSet, symbolSum } from "./type_constructors.js";
import { makeTypeConstructor } from "../meta/type_constructor.js";
import { Type, UUID } from "../primitives/primitive_types.js";
import { symbolDict, symbolFunction, symbolList, symbolProduct, symbolSet, symbolSum } from "./type_constructors.js";
const mkType = getDefaultTypeParser();
export const fnType = makeTypeConstructor(symbolFunction)(2);
export const sumType = makeTypeConstructor(symbolSum)(2);
export const prodType = makeTypeConstructor(symbolProduct)(2);
export const lsType = makeTypeConstructor(symbolList)(1);
export const setType = makeTypeConstructor(symbolSet)(1);
export const dictType = makeTypeConstructor(symbolDict)(2);
export const ModuleStructuralSymbols = [
{ i: symbolSet , t: UUID },
@ -13,11 +18,15 @@ export const ModuleStructuralSymbols = [
{ i: symbolFunction , t: UUID },
];
const unaryTypeConstructor = fnType(_ => Type)(_ => Type);
const binaryTypeConstructor = fnType(_ => Type)(_ => unaryTypeConstructor);
export const ModuleTypeConstructors = [
{ i: setType , t: mkType("Type -> Type") },
{ i: lsType , t: mkType("Type -> Type") },
{ i: prodType , t: mkType("Type -> Type -> Type") },
{ i: sumType , t: mkType("Type -> Type -> Type") },
{ i: dictType , t: mkType("Type -> Type -> Type") },
{ i: fnType , t: mkType("Type -> Type -> Type") },
{ i: setType , t: unaryTypeConstructor },
{ i: lsType , t: unaryTypeConstructor },
{ i: prodType , t: binaryTypeConstructor },
{ i: sumType , t: binaryTypeConstructor },
{ i: dictType , t: binaryTypeConstructor },
{ i: fnType , t: binaryTypeConstructor },
];

View file

@ -1,6 +1,5 @@
import { inspect } from 'node:util';
import { symbolDict, symbolFunction, symbolList, symbolProduct, symbolSum } from '../structures/type_constructors.js';
import { symbolSet } from "../structures/type_constructors.js";
import { symbolDict, symbolFunction, symbolList, symbolProduct, symbolSum, symbolSet } from '../structures/type_constructors.js';
import { getHumanReadableName } from '../primitives/symbol.js';
import { getSymbol } from '../primitives/type.js';

View file

@ -3,19 +3,19 @@
import createRBTree from "functional-red-black-tree";
import { inspect } from "util";
function defaultPrintf(depth, options, inspect) {
const entries = [];
this.tree.forEach((key, val) => { entries.push(`${inspect(key)} => ${inspect(val)}`); });
return `RBTree(${this.tree.length}) {${entries.join(', ')}}`;
}
export class RBTreeWrapper {
constructor(tree) {
constructor(tree, printf = defaultPrintf) {
this.tree = tree;
this[inspect.custom] = printf;
}
static new(compareFn) {
return new RBTreeWrapper(createRBTree(compareFn))
}
// pretty print to console
[inspect.custom](depth, options, inspect) {
const entries = [];
this.tree.forEach((key, val) => { entries.push(`${inspect(key)} => ${inspect(val)}`); });
return `RBTree(${this.tree.length}) {${entries.join(', ')}}`;
}
}

View file

@ -18,3 +18,7 @@ export const memoize = callback => {
return result;
};
};
export const indent = (multiline, n) => {
return multiline.split('\n').map(line => ' '.repeat(n)+line).join('\n');
}

View file

@ -3,7 +3,7 @@ import { compareTypes } from "../lib/compare/type.js";
import { makeGeneric, substitute, unify } from "../lib/generics/generics.js";
import { Double, Int, Unit } from "../lib/primitives/primitive_types.js";
import { UNBOUND_SYMBOLS } from "../lib/primitives/typevars.js";
import { fnType, lsType, prodType, sumType, setType } from "../lib/structures/type_constructors.js";
import { fnType, lsType, prodType, sumType, setType } from "../lib/structures/type_constructors.types.js";
import { prettyT } from "../lib/util/pretty.js";
// some recursive types: