turn the function for creating new types (or type constructors) into a DOPE function
This commit is contained in:
parent
d8ca2f3999
commit
a0e3aa0cb3
12 changed files with 112 additions and 58 deletions
|
|
@ -23,7 +23,7 @@ export const makeGeneric = callback => {
|
||||||
|
|
||||||
// From the given set of type variables, return only those that occur in the given type.
|
// From the given set of type variables, return only those that occur in the given type.
|
||||||
export const occurring = (type, typeVars) => {
|
export const occurring = (type, typeVars) => {
|
||||||
console.log("occurring", type, typeVars);
|
// console.log("occurring", type);
|
||||||
|
|
||||||
if (typeVars.has(type)) {
|
if (typeVars.has(type)) {
|
||||||
// type IS a type variable:
|
// type IS a type variable:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { constructSymbol, eqSymbol, getName } from "../primitives/symbol.js";
|
import { eqSymbol, getName } from "../primitives/symbol.js";
|
||||||
import { Bool, SymbolT, Type } from "../primitives/types.js";
|
import { Bool, SymbolT, Type } from "../primitives/types.js";
|
||||||
import { String } from "../structures/list.js";
|
import { String } from "../structures/list.js";
|
||||||
import { typedFnType } from "../structures/types.js";
|
import { typedFnType } from "../structures/types.js";
|
||||||
|
|
@ -6,11 +6,11 @@ import { typedFnType } from "../structures/types.js";
|
||||||
export const ModuleSymbol = {l:[
|
export const ModuleSymbol = {l:[
|
||||||
{i: SymbolT, t: Type},
|
{i: SymbolT, t: Type},
|
||||||
|
|
||||||
...typedFnType(constructSymbol, fnType =>
|
// ...typedFnType(constructSymbol, fnType =>
|
||||||
fnType
|
// fnType
|
||||||
(String)
|
// (String)
|
||||||
(SymbolT)
|
// (SymbolT)
|
||||||
),
|
// ),
|
||||||
|
|
||||||
...typedFnType(getName, fnType =>
|
...typedFnType(getName, fnType =>
|
||||||
fnType
|
fnType
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// The functions are only defined here. For their types, see lib/symbol.js
|
// The functions are only defined here. For their types, see lib/symbol.js
|
||||||
|
|
||||||
// The point of having an explicit constructor for SymbolT, is that we can later swap it out for something that is (de-)serializable.
|
// Cannot turn the constructor into a DOPE function, because it is NOT PURE:
|
||||||
export const constructSymbol = name => Symbol(name);
|
// export const constructSymbol = name => Symbol(name);
|
||||||
|
|
||||||
export const getName = symbol => symbol.description;
|
export const getName = symbol => symbol.description;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,37 @@
|
||||||
// to break up dependency cycles, primitive types are defined in their own JS module
|
// to break up dependency cycles, primitive types are defined in their own JS module
|
||||||
|
|
||||||
import { makeTypeConstructor } from "../type_constructor.js";
|
import { makeTypeConstructor } from "../type_constructor.js";
|
||||||
import { constructSymbol } from "./symbol.js";
|
|
||||||
|
|
||||||
export const Int = makeTypeConstructor(constructSymbol('Int'), 0);
|
const SymbolInt = Symbol('Int');
|
||||||
export const Bool = makeTypeConstructor(constructSymbol('Bool'), 0);
|
const SymbolBool = Symbol('Bool');
|
||||||
export const Double = makeTypeConstructor(constructSymbol('Double'), 0);
|
const SymbolDouble = Symbol('Double');
|
||||||
export const Byte = makeTypeConstructor(constructSymbol('Byte'), 0);
|
const SymbolByte = Symbol('Byte');
|
||||||
export const Char = makeTypeConstructor(constructSymbol('Char'), 0);
|
const SymbolChar = Symbol('Char');
|
||||||
|
const SymbolUnit = Symbol('Unit');
|
||||||
|
const SymbolSymbol = Symbol('Symbol');
|
||||||
|
const SymbolType = Symbol('Type');
|
||||||
|
|
||||||
|
export const Int = makeTypeConstructor(SymbolInt)(0);
|
||||||
|
export const Bool = makeTypeConstructor(SymbolBool)(0);
|
||||||
|
export const Double = makeTypeConstructor(SymbolDouble)(0);
|
||||||
|
export const Byte = makeTypeConstructor(SymbolByte)(0);
|
||||||
|
export const Char = makeTypeConstructor(SymbolChar)(0);
|
||||||
|
|
||||||
// Unit type has only 1 instance, the empty tuple.
|
// Unit type has only 1 instance, the empty tuple.
|
||||||
export const Unit = makeTypeConstructor(constructSymbol('Unit'), 0);
|
export const Unit = makeTypeConstructor(SymbolUnit)(0);
|
||||||
|
|
||||||
export const SymbolT = makeTypeConstructor(constructSymbol('Symbol'), 0);
|
export const SymbolT = makeTypeConstructor(SymbolSymbol)(0);
|
||||||
|
|
||||||
export const Type = makeTypeConstructor(constructSymbol('Type'), 0);
|
export const Type = makeTypeConstructor(SymbolType)(0);
|
||||||
|
|
||||||
|
|
||||||
|
export const ModuleSymbols = {l:[
|
||||||
|
{i: SymbolInt , t: SymbolT},
|
||||||
|
{i: SymbolBool , t: SymbolT},
|
||||||
|
{i: SymbolDouble, t: SymbolT},
|
||||||
|
{i: SymbolByte , t: SymbolT},
|
||||||
|
{i: SymbolChar , t: SymbolT},
|
||||||
|
{i: SymbolUnit , t: SymbolT},
|
||||||
|
{i: SymbolSymbol, t: SymbolT},
|
||||||
|
{i: SymbolType , t: SymbolT},
|
||||||
|
]};
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { select, number } from '@inquirer/prompts';
|
import { select, number, input } from '@inquirer/prompts';
|
||||||
import { ModulePoint } from "../lib/point.js";
|
import { ModulePoint } from "../lib/point.js";
|
||||||
import { DefaultMap } from "../util/defaultmap.js";
|
import { DefaultMap } from "../util/defaultmap.js";
|
||||||
import { pretty } from '../util/pretty.js';
|
import { pretty } from '../util/pretty.js';
|
||||||
import { isFunction, prettyT } from '../structures/types.js';
|
import { isFunction, prettyT } from '../structures/types.js';
|
||||||
import { ModuleStd } from '../stdlib.js';
|
import { ModuleStd } from '../stdlib.js';
|
||||||
import { Double, Int, Type } from "../primitives/types.js";
|
import { Double, Int, SymbolT, Type } from "../primitives/types.js";
|
||||||
import { eqType } from '../type.js';
|
import { eqType } from '../type.js';
|
||||||
|
|
||||||
class Context {
|
class Context {
|
||||||
|
|
@ -260,33 +260,49 @@ async function functionOptions(fn, fnT) {
|
||||||
async function callFunction(fn, fnT) {
|
async function callFunction(fn, fnT) {
|
||||||
const {strI, strT} = prettyIT({i: fn, t: fnT});
|
const {strI, strT} = prettyIT({i: fn, t: fnT});
|
||||||
const inType = fnT.params[0];
|
const inType = fnT.params[0];
|
||||||
const choice = await (async () => {
|
const choice = await select({
|
||||||
|
message: `select parameter for function ${strI} :: ${strT}`,
|
||||||
|
choices: [
|
||||||
|
"(go back)",
|
||||||
|
"(new)",
|
||||||
|
... [...ctx.instances.getdefault(inType)].flatMap(i => toChoices([i, ctx.types.getdefault(i)])),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
let i;
|
||||||
|
if (choice === "(go back)") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (choice === "(new)") {
|
||||||
if (eqType(inType)(Int)) {
|
if (eqType(inType)(Int)) {
|
||||||
const n = await number({
|
const n = await number({
|
||||||
message: `enter an integer (leave empty to go back):`,
|
message: `enter an integer (leave empty to go back):`,
|
||||||
step: 1, // only integers
|
step: 1, // only integers
|
||||||
});
|
});
|
||||||
return (n === undefined) ? "(go back)" : {i: BigInt(n), t: Int};
|
if (n === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i = BigInt(n);
|
||||||
}
|
}
|
||||||
if (eqType(inType)(Double)) {
|
else if (eqType(inType)(Double)) {
|
||||||
const n = await number({
|
const n = await number({
|
||||||
message: `enter a number (leave empty to go back):`,
|
message: `enter a number (leave empty to go back):`,
|
||||||
step: 'any',
|
step: 'any',
|
||||||
});
|
});
|
||||||
return (n === undefined) ? "(go back)" : {i: n, t: Int};
|
if (n === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
i = n;
|
||||||
|
}
|
||||||
|
else if (eqType(inType)(SymbolT)) {
|
||||||
|
console.log("Note: you are creating a new Symbol. Even if the description matches that of another symbol (e.g., \"Int\"), a new Symbol will be created that is unique and only equal to itself.");
|
||||||
|
const symbolDescr = await input({message: "enter symbol description:"});
|
||||||
|
i = Symbol(symbolDescr);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("no prompt handler for creating new", prettyT(inType));
|
||||||
|
return callFunction(fn, fnT);
|
||||||
}
|
}
|
||||||
return select({
|
|
||||||
message: `select parameter for function ${strI} :: ${strT}`,
|
|
||||||
choices: [
|
|
||||||
"(go back)",
|
|
||||||
... [...ctx.instances.getdefault(inType)].flatMap(i => toChoices([i, ctx.types.getdefault(i)])),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
if (choice === "(go back)") {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const {i, t} = choice;
|
|
||||||
await apply(i, fn, fnT);
|
await apply(i, fn, fnT);
|
||||||
return callFunction(fn, fnT);
|
return callFunction(fn, fnT);
|
||||||
}
|
}
|
||||||
|
|
@ -327,6 +343,7 @@ async function transform(i, t) {
|
||||||
if (choice === "(go back)") {
|
if (choice === "(go back)") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {i:fn,t:fnT} = choice;
|
const {i:fn,t:fnT} = choice;
|
||||||
await apply(i, fn, fnT);
|
await apply(i, fn, fnT);
|
||||||
return transform(i, t);
|
return transform(i, t);
|
||||||
|
|
@ -334,6 +351,7 @@ async function transform(i, t) {
|
||||||
|
|
||||||
async function apply(i, fn, fnT) {
|
async function apply(i, fn, fnT) {
|
||||||
const result = fn(i);
|
const result = fn(i);
|
||||||
|
// console.log(fn, '(', i, ')', '=', result);
|
||||||
const resultType = fnT.params[1];
|
const resultType = fnT.params[1];
|
||||||
// update context with newly produced value
|
// update context with newly produced value
|
||||||
ctx = ctx.addToCtx({i: result, t: resultType});
|
ctx = ctx.addToCtx({i: result, t: resultType});
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { ModuleByte } from "./primitives/byte.js";
|
||||||
import { ModuleChar } from "./primitives/char.js";
|
import { ModuleChar } from "./primitives/char.js";
|
||||||
import { ModuleDouble } from "./primitives/double.js";
|
import { ModuleDouble } from "./primitives/double.js";
|
||||||
import { ModuleInt } from "./primitives/int.js";
|
import { ModuleInt } from "./primitives/int.js";
|
||||||
|
import { ModuleSymbols } from "./primitives/types.js";
|
||||||
import { ModuleUnit } from "./primitives/unit.js";
|
import { ModuleUnit } from "./primitives/unit.js";
|
||||||
import { ModuleFunction } from "./structures/function.js";
|
import { ModuleFunction } from "./structures/function.js";
|
||||||
import { ModuleList } from "./structures/list.js";
|
import { ModuleList } from "./structures/list.js";
|
||||||
|
|
@ -18,6 +19,7 @@ export const ModuleStd = {l:[
|
||||||
...ModuleTyped.l,
|
...ModuleTyped.l,
|
||||||
|
|
||||||
...ModuleTypeConstructor.l,
|
...ModuleTypeConstructor.l,
|
||||||
|
...ModuleSymbols.l,
|
||||||
|
|
||||||
// Primitive types
|
// Primitive types
|
||||||
...ModuleBool.l,
|
...ModuleBool.l,
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,6 @@ import { Type } from "../primitives/types.js";
|
||||||
import { typedFnType } from "./types.js";
|
import { typedFnType } from "./types.js";
|
||||||
import { fnType } from "./types.js";
|
import { fnType } from "./types.js";
|
||||||
|
|
||||||
// export const pprintFn = fnT => ``
|
|
||||||
|
|
||||||
export const ModuleFunction = {l:[
|
export const ModuleFunction = {l:[
|
||||||
// binary type constructor: Type -> Type -> Type
|
// binary type constructor: Type -> Type -> Type
|
||||||
...typedFnType(fnType, fnType => fnType
|
...typedFnType(fnType, fnType => fnType
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { sumType, prodType, fnType } from "./types.js";
|
||||||
|
|
||||||
|
|
||||||
export const createNominalADT = symbol => variants => {
|
export const createNominalADT = symbol => variants => {
|
||||||
|
makeTypeConstructor(symbol, 0, )
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createNominalADTFnType =
|
export const createNominalADTFnType =
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { getSymbol, makeTypeConstructor } from "../type_constructor.js";
|
||||||
// It is a cheap workaround for JS lacking customizable hash-functions and equality-testing-functions.
|
// It is a cheap workaround for JS lacking customizable hash-functions and equality-testing-functions.
|
||||||
// This same pattern is repeated throughout the code for all non-nullary type constructors (list, sum, product, ...)
|
// This same pattern is repeated throughout the code for all non-nullary type constructors (list, sum, product, ...)
|
||||||
const symbolFunction = Symbol('Function');
|
const symbolFunction = Symbol('Function');
|
||||||
export const fnType = makeTypeConstructor(symbolFunction, 2);
|
export const fnType = makeTypeConstructor(symbolFunction)(2);
|
||||||
|
|
||||||
export const isFunction = type => getSymbol(type) === symbolFunction;
|
export const isFunction = type => getSymbol(type) === symbolFunction;
|
||||||
|
|
||||||
|
|
@ -40,25 +40,26 @@ export const typedFnType2 = callback => {
|
||||||
// Sum type
|
// Sum type
|
||||||
|
|
||||||
const symbolSum = Symbol("Sum");
|
const symbolSum = Symbol("Sum");
|
||||||
export const sumType = makeTypeConstructor(symbolSum, 2);
|
export const sumType = makeTypeConstructor(symbolSum)(2);
|
||||||
|
|
||||||
// Product type
|
// Product type
|
||||||
|
|
||||||
const symbolProduct = Symbol("Product");
|
const symbolProduct = Symbol("Product");
|
||||||
export const prodType = makeTypeConstructor(symbolProduct, 2);
|
export const prodType = makeTypeConstructor(symbolProduct)(2);
|
||||||
|
|
||||||
// List type
|
// List type
|
||||||
|
|
||||||
const symbolList = Symbol('List');
|
const symbolList = Symbol('List');
|
||||||
export const lsType = makeTypeConstructor(symbolList, 1);
|
export const lsType = makeTypeConstructor(symbolList)(1);
|
||||||
|
|
||||||
// Set type
|
// Set type
|
||||||
|
|
||||||
const symbolSet = Symbol('Set');
|
const symbolSet = Symbol('Set');
|
||||||
export const setType = makeTypeConstructor(symbolSet, 1);
|
export const setType = makeTypeConstructor(symbolSet)(1);
|
||||||
|
|
||||||
// Pretty print type
|
// Pretty print type
|
||||||
export function prettyT(type) {
|
export function prettyT(type) {
|
||||||
|
// console.log("pretty:", type);
|
||||||
if (type.typeVars) {
|
if (type.typeVars) {
|
||||||
if (type.typeVars.size > 0) {
|
if (type.typeVars.size > 0) {
|
||||||
return `∀${[...type.typeVars].map(prettyT).join(", ")}: ${prettyT(type.type)}`;
|
return `∀${[...type.typeVars].map(prettyT).join(", ")}: ${prettyT(type.type)}`;
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { eqDictType } from "../typeclasses/eq_type.js";
|
||||||
import { fnType, setType } from "./types.js";
|
import { fnType, setType } from "./types.js";
|
||||||
|
|
||||||
const symbolVersioned = Symbol("Versioned");
|
const symbolVersioned = Symbol("Versioned");
|
||||||
export const versionedType = makeTypeConstructor(symbolVersioned, 1);
|
export const versionedType = makeTypeConstructor(symbolVersioned)(1);
|
||||||
|
|
||||||
|
|
||||||
export const constructor = parents => value => {
|
export const constructor = parents => value => {
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,30 @@
|
||||||
import { DefaultMap } from "./util/defaultmap.js";
|
import { DefaultMap } from "./util/defaultmap.js";
|
||||||
|
|
||||||
// Creates a new nominal type
|
const nullaryTypeConstructors = new DefaultMap(symbol => ({symbol, params: []})); // symbol -> 0-ary type constructor (= a type, basically)
|
||||||
export const makeTypeConstructor = (name, n_ary, params = []) => {
|
|
||||||
if (n_ary === 0) {
|
const makeTypeConstructorInternal = (symbol, n_ary, params = []) => {
|
||||||
return { symbol: Symbol(name), params };
|
// console.log("n_ary:", n_ary);
|
||||||
|
if (n_ary === 0 || n_ary === 0n) {
|
||||||
|
// a bit dirty, but otherwise OK
|
||||||
|
if (params.length > 0) {
|
||||||
|
const result = { symbol, params };
|
||||||
|
// console.log("result:", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const result = nullaryTypeConstructors.getdefault(symbol, true)
|
||||||
|
// console.log("result:", result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const m = new DefaultMap(typeParam => makeTypeConstructor(name, n_ary - 1, params.concat([typeParam])));
|
const m = new DefaultMap(typeParam => makeTypeConstructorInternal(symbol, n_ary - 1, params.concat([typeParam])));
|
||||||
return typeParam => m.getdefault(typeParam, true);
|
return typeParam => m.getdefault(typeParam, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Creates a new nominal type
|
||||||
|
export const makeTypeConstructor = symbol => nAry => makeTypeConstructorInternal(symbol, nAry);
|
||||||
|
|
||||||
export const getSymbol = type => type.symbol;
|
export const getSymbol = type => type.symbol;
|
||||||
export const getParams = type => ({ l: type.params });
|
export const getParams = type => ({ l: type.params });
|
||||||
|
|
|
||||||
|
|
@ -29,15 +29,15 @@ export const ModuleEq = {l:[
|
||||||
|
|
||||||
const eq = x => y => deepEqual(x,y);
|
const eq = x => y => deepEqual(x,y);
|
||||||
|
|
||||||
const EqDict = {eq};
|
const eqDict = {eq};
|
||||||
|
|
||||||
export const EqInstances = new Map([
|
export const EqInstances = new Map([
|
||||||
[Int , EqDict],
|
[Int , eqDict],
|
||||||
[Bool , EqDict],
|
[Bool , eqDict],
|
||||||
[Double , EqDict],
|
[Double , eqDict],
|
||||||
[Byte , EqDict],
|
[Byte , eqDict],
|
||||||
[Char , EqDict],
|
[Char , eqDict],
|
||||||
[Unit , EqDict],
|
[Unit , eqDict],
|
||||||
[Type , EqDict],
|
[Type , eqDict],
|
||||||
[SymbolT, EqDict],
|
[SymbolT, eqDict],
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue