replace 'prompt' example by 'environment'
This commit is contained in:
parent
f8008aa25d
commit
b1c2e7836d
15 changed files with 317 additions and 517 deletions
|
|
@ -1,27 +1,265 @@
|
||||||
import { compareTypes } from "../lib/compare/type.js";
|
import { number, select } from "@inquirer/prompts";
|
||||||
import { ModuleStd } from "../lib/stdlib.js";
|
|
||||||
import { emptyDict, get, set } from "../lib/structures/dict.js";
|
|
||||||
import { emptySet, add } from "../lib/structures/set.js";
|
|
||||||
import { makeCompareFn } from "../lib/compare/dynamic.js"
|
|
||||||
import { Type } from "../lib/primitives/primitive_types.js";
|
|
||||||
|
|
||||||
console.log(ModuleStd);
|
import { makeCompareFn } from "../lib/compare/dynamic.js";
|
||||||
|
import { compareTypes } from "../lib/compare/type.js";
|
||||||
|
import { getInst, getType, newDynamic } from "../lib/primitives/dynamic.js";
|
||||||
|
import { Bool, Double, Int, Type, UUID } from "../lib/primitives/primitive_types.js";
|
||||||
|
import { eqType, getSymbol } from "../lib/primitives/type.js";
|
||||||
|
import { ModuleStd } from "../lib/stdlib.js";
|
||||||
|
import { emptyDict, get, set, fold as foldDict } from "../lib/structures/dict.js";
|
||||||
|
import { fold as foldList } from "../lib/structures/list.js";
|
||||||
|
import { add, emptySet, fold as foldSet } from "../lib/structures/set.js";
|
||||||
|
import { symbolFunction } from "../lib/structures/type_constructors.js";
|
||||||
|
import { pretty, prettyT } from "../lib/util/pretty.js";
|
||||||
|
import { genUUID } from "../lib/util/random.js";
|
||||||
|
import { assignFn, unify } from "../lib/generics/generics.js";
|
||||||
|
|
||||||
|
// console.log(ModuleStd);
|
||||||
|
|
||||||
const addEntry = dict => i => t => {
|
const addEntry = dict => i => t => {
|
||||||
const setOfInstances = get(dict)(t) || emptySet(makeCompareFn(t));
|
const setOfInstances = get(dict)(t) || emptySet(makeCompareFn(t));
|
||||||
return set(dict)(t)(add(setOfInstances)(i));
|
return set(dict)(t)(add(setOfInstances)(i));
|
||||||
}
|
};
|
||||||
|
|
||||||
const typeDict = ModuleStd.reduce((typeDict, {i, t}) => {
|
const typeDict = foldList(typeDict => dynamic => {
|
||||||
try {
|
try {
|
||||||
|
const t = getType(dynamic);
|
||||||
// add instance to type:
|
// add instance to type:
|
||||||
return addEntry(
|
return addEntry(
|
||||||
addEntry(typeDict)(i)(t)
|
addEntry(typeDict)(getInst(dynamic))(t)
|
||||||
)(t)(Type);
|
)(t)(Type);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('warning:',e.message);
|
console.log('warning:', e.message);
|
||||||
return typeDict;
|
return typeDict;
|
||||||
}
|
}
|
||||||
}, emptyDict(compareTypes));
|
})(emptyDict(compareTypes))(ModuleStd);
|
||||||
|
|
||||||
console.log(typeDict);
|
const functions = foldList(functions => dynamic => {
|
||||||
|
if (getSymbol(getType(dynamic)) === symbolFunction) {
|
||||||
|
return [...functions, dynamic];
|
||||||
|
}
|
||||||
|
return functions;
|
||||||
|
})([])(ModuleStd);
|
||||||
|
|
||||||
|
// console.log(typeDict);
|
||||||
|
// console.log(functions);
|
||||||
|
|
||||||
|
const defaultSelectOptions = {
|
||||||
|
pageSize: 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectInstance = async (type, msg) => {
|
||||||
|
const instances = get(typeDict)(type) || emptySet(makeCompareFn(type));
|
||||||
|
const choices = foldSet(acc => instance => {
|
||||||
|
return [...acc, {
|
||||||
|
value: instance,
|
||||||
|
name: pretty(instance),
|
||||||
|
}];
|
||||||
|
})(["(go back)", "(new)"])(instances);
|
||||||
|
const choice = await select({
|
||||||
|
message: `${msg} ${pretty(type)}:`,
|
||||||
|
choices,
|
||||||
|
...defaultSelectOptions,
|
||||||
|
});
|
||||||
|
if (choice === "(go back)") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (choice === "(new)") {
|
||||||
|
return createInstance(type);
|
||||||
|
}
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
const listInstances = async type => {
|
||||||
|
const choice = await selectInstance(type, "instances of");
|
||||||
|
if (choice === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await proxyDynamic(newDynamic(choice)(type));
|
||||||
|
return await listInstances(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
const proxyDynamic = async dynamic => {
|
||||||
|
const type = getType(dynamic)
|
||||||
|
if (eqType(type)(Type)) {
|
||||||
|
return listTypeOptions(getInst(dynamic));
|
||||||
|
}
|
||||||
|
if (getSymbol(type) === symbolFunction) {
|
||||||
|
return listFunctionOptions(dynamic);
|
||||||
|
}
|
||||||
|
return listInstanceOptions(dynamic);
|
||||||
|
}
|
||||||
|
|
||||||
|
const listTypeOptions = async type => {
|
||||||
|
const choice = await select({
|
||||||
|
message: `type ${pretty(type)}:`,
|
||||||
|
choices: [
|
||||||
|
"(go back)",
|
||||||
|
"new",
|
||||||
|
"list instances",
|
||||||
|
"treat as instance",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (choice === "(go back)") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (choice === "new") {
|
||||||
|
const i = await createInstance(type);
|
||||||
|
if (i !== undefined) {
|
||||||
|
const dynamic = newDynamic(i)(type);
|
||||||
|
await proxyDynamic(dynamic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (choice === "list instances") {
|
||||||
|
await listInstances(type);
|
||||||
|
}
|
||||||
|
if (choice === "treat as instance") {
|
||||||
|
await listInstanceOptions(newDynamic(type)(Type));
|
||||||
|
}
|
||||||
|
return await listTypeOptions(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
const listInstanceOptions = async dynamic => {
|
||||||
|
const choice = await select({
|
||||||
|
message: `instance ${pretty(dynamic)}:`,
|
||||||
|
choices: [
|
||||||
|
"(go back)",
|
||||||
|
"transform",
|
||||||
|
"get type",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (choice === "(go back)") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (choice === "transform") {
|
||||||
|
await transform(dynamic);
|
||||||
|
}
|
||||||
|
if (choice === "get type") {
|
||||||
|
await listTypeOptions(getType(dynamic));
|
||||||
|
}
|
||||||
|
return await listInstanceOptions(dynamic);
|
||||||
|
}
|
||||||
|
|
||||||
|
const listFunctionOptions = async dynamic => {
|
||||||
|
const choice = await select({
|
||||||
|
message: `function ${pretty(dynamic)}:`,
|
||||||
|
choices: [
|
||||||
|
"(go back)",
|
||||||
|
"call",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (choice === "(go back)") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (choice === "call") {
|
||||||
|
await call(dynamic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createInstance = async type => {
|
||||||
|
if (eqType(type)(Int)) {
|
||||||
|
const n = await number({
|
||||||
|
message: `enter an integer (leave empty to go back):`,
|
||||||
|
step: 1, // only integers
|
||||||
|
});
|
||||||
|
if (n === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return BigInt(n);
|
||||||
|
}
|
||||||
|
if (eqType(type)(Double)) {
|
||||||
|
const n = await number({
|
||||||
|
message: `enter a number (leave empty to go back):`,
|
||||||
|
step: 'any',
|
||||||
|
});
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
if (eqType(type)(Bool)) {
|
||||||
|
const b = await select({
|
||||||
|
message: `select:`,
|
||||||
|
choices: [
|
||||||
|
{value: false, name: 'false'},
|
||||||
|
{value: true, name: 'true'},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
if (eqType(type)(UUID)) {
|
||||||
|
const descr = await input({message: "enter UUID description:"});
|
||||||
|
return descr + '__' + genUUID(16);
|
||||||
|
}
|
||||||
|
console.log("no prompt handler for creating new", prettyT(type));
|
||||||
|
};
|
||||||
|
|
||||||
|
const transform = async dynamic => {
|
||||||
|
const enabled = foldList(enabled => fun => {
|
||||||
|
try {
|
||||||
|
const outType = assignFn(getType(fun), getType(dynamic));
|
||||||
|
return [...enabled, {fun, outType}];
|
||||||
|
} catch (e) {
|
||||||
|
// console.log('warning:', e);
|
||||||
|
return enabled;
|
||||||
|
}
|
||||||
|
})([])(functions);
|
||||||
|
|
||||||
|
const choice = await select({
|
||||||
|
message: `select:`,
|
||||||
|
choices: [
|
||||||
|
"(go back)",
|
||||||
|
...enabled.map(({fun, outType}) => ({
|
||||||
|
value: {fun, outType},
|
||||||
|
name: `${getInst(fun).name} ${pretty(getInst(dynamic))} :: ${pretty(outType)}`,
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (choice === "(go back)") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const {fun, outType} = choice;
|
||||||
|
const outValue = getInst(fun)(getInst(dynamic));
|
||||||
|
await proxyDynamic(newDynamic(outValue)(outType));
|
||||||
|
return transform(dynamic);
|
||||||
|
}
|
||||||
|
|
||||||
|
const call = async dynamic => {
|
||||||
|
const funType = getType(dynamic);
|
||||||
|
const allTypes = get(typeDict)(Type);
|
||||||
|
|
||||||
|
// compatible input types
|
||||||
|
const inTypes = foldSet(types => inType => {
|
||||||
|
try {
|
||||||
|
const outType = assignFn(funType, inType); // may throw
|
||||||
|
return [...types, {inType, outType}];
|
||||||
|
} catch (e) {
|
||||||
|
return types;
|
||||||
|
}
|
||||||
|
})([])(allTypes);
|
||||||
|
|
||||||
|
const choice = inTypes.length === 1
|
||||||
|
? inTypes[0]
|
||||||
|
: await select({
|
||||||
|
message: `types servable as input to ${pretty(funType)}:`,
|
||||||
|
choices: [
|
||||||
|
"(go back)",
|
||||||
|
...inTypes.map(({inType, outType}) => ({
|
||||||
|
value: {inType, outType},
|
||||||
|
name: pretty(inType),
|
||||||
|
})),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
if (choice === "(go back)") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const inValue = await selectInstance(choice.inType, "select input value of type");
|
||||||
|
if (inValue === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const outValue = getInst(dynamic)(inValue);
|
||||||
|
await proxyDynamic(newDynamic(outValue)(choice.outType));
|
||||||
|
return call(dynamic);
|
||||||
|
}
|
||||||
|
|
||||||
|
await listInstances(Type);
|
||||||
|
// await transform(newDynamic(5)(Double));
|
||||||
|
|
|
||||||
|
|
@ -1,449 +0,0 @@
|
||||||
import { select, number, input } from '@inquirer/prompts';
|
|
||||||
import { ModulePoint } from "../lib/point.js";
|
|
||||||
import { DefaultMap } from "../lib/util/defaultmap.js";
|
|
||||||
import { pretty } from '../lib/util/pretty.js';
|
|
||||||
import { isFunction } from '../structures/types.js';
|
|
||||||
import { ModuleStd } from '../lib/stdlib.js';
|
|
||||||
import { Double, GenericType, Int, UUID, Type } from "../primitives/types.js";
|
|
||||||
import { eqType } from '../primitives/type.js';
|
|
||||||
import { Top } from "../primitives/types.js";
|
|
||||||
import { assignFn, makeGeneric, onlyOccurring } from '../lib/generics/generics.js';
|
|
||||||
import { prettyT } from '../lib/util/pretty.js';
|
|
||||||
import { genUUID } from '../lib/util/random.js';
|
|
||||||
|
|
||||||
|
|
||||||
// import {emitKeypressEvents} from 'node:readline';
|
|
||||||
|
|
||||||
// // Configure readline to read from stdin
|
|
||||||
// emitKeypressEvents(process.stdin);
|
|
||||||
// process.stdin.setRawMode(true);
|
|
||||||
|
|
||||||
// console.log('Press any key (ESC to exit)...');
|
|
||||||
|
|
||||||
// process.stdin.on('keypress', (str, key) => {
|
|
||||||
// if (key.name === 'escape') {
|
|
||||||
// console.log('Escape key pressed!');
|
|
||||||
// process.exit();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
const prettyIT = ({i, t}) => ({
|
|
||||||
strI: isType(i) ? prettyT(i) : pretty(i),
|
|
||||||
strT: prettyT(t),
|
|
||||||
// strI: pretty(i),
|
|
||||||
// strT: pretty(t),
|
|
||||||
});
|
|
||||||
|
|
||||||
const isType = i => i.typeVars || i.symbol;
|
|
||||||
// ctx.types.getdefault(i).has(Type)
|
|
||||||
// || ctx.types.getdefault(i).has(GenericType);
|
|
||||||
|
|
||||||
class Context {
|
|
||||||
constructor(mod) {
|
|
||||||
this.mod = mod;
|
|
||||||
this.types = new DefaultMap(() => new Set()); // instance to type
|
|
||||||
this.instances = new DefaultMap(() => new Set()); // type to instance
|
|
||||||
|
|
||||||
for (const {i, t} of mod.l) {
|
|
||||||
const {strI, strT} = prettyIT({i,t})
|
|
||||||
// console.log(strI, '::', strT);
|
|
||||||
|
|
||||||
this.types.getdefault(i, true).add(t);
|
|
||||||
this.types.getdefault(i, true).add(Top);
|
|
||||||
if (t.typeVars) {
|
|
||||||
// console.log("generic:", prettyT(t));
|
|
||||||
this.types.getdefault(t, true).add(GenericType);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// console.log("non-generic:", prettyT(t));
|
|
||||||
this.types.getdefault(t, true).add(Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.instances.getdefault(t, true).add(i);
|
|
||||||
this.instances.getdefault(Top, true).add(i);
|
|
||||||
}
|
|
||||||
const addIfFunctionType = (t, originalT, add) => {
|
|
||||||
if (isFunction(t)) {
|
|
||||||
for (const fn of this.instances.getdefault(originalT)) {
|
|
||||||
add(fn);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.functions = [];
|
|
||||||
for (const type of this.instances.getdefault(Type)) {
|
|
||||||
addIfFunctionType(type, type, fn => this.functions.push({fn, type}));
|
|
||||||
}
|
|
||||||
this.genericFunctions = [];
|
|
||||||
for (const genericType of this.instances.getdefault(GenericType)) {
|
|
||||||
addIfFunctionType(genericType.type, genericType, fn => this.genericFunctions.push({fn, genericType}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addToCtx({i, t}) {
|
|
||||||
return new Context({l:[
|
|
||||||
...this.mod.l,
|
|
||||||
{i, t},
|
|
||||||
]});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let ctx = new Context({l:[
|
|
||||||
...ModuleStd.l,
|
|
||||||
...ModulePoint.l,
|
|
||||||
]});
|
|
||||||
|
|
||||||
const toChoices = ([i, types]) => {
|
|
||||||
return [...types].map(t => {
|
|
||||||
const {strI, strT} = prettyIT({i, t});
|
|
||||||
return {
|
|
||||||
value: {i, t},
|
|
||||||
name: strI,
|
|
||||||
description: ` :: ${strT}`,
|
|
||||||
short: `${strI} :: ${strT}`,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
async function topPrompt() {
|
|
||||||
const action = await select({
|
|
||||||
message: "What do you want to do?",
|
|
||||||
choices: [
|
|
||||||
"list all types",
|
|
||||||
"list all generic types",
|
|
||||||
"list all functions",
|
|
||||||
"list all",
|
|
||||||
],
|
|
||||||
});
|
|
||||||
if (action === "list all types") {
|
|
||||||
await listInstances(Type);
|
|
||||||
}
|
|
||||||
if (action === "list all generic types") {
|
|
||||||
await listInstances(GenericType);
|
|
||||||
}
|
|
||||||
if (action === "list all functions") {
|
|
||||||
await listAllFunctions();
|
|
||||||
}
|
|
||||||
if (action === "list all") {
|
|
||||||
await listAll();
|
|
||||||
}
|
|
||||||
return topPrompt();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function listAllFunctions() {
|
|
||||||
const choice = await select({
|
|
||||||
message: "select function:",
|
|
||||||
choices: [
|
|
||||||
"(go back)",
|
|
||||||
...ctx.functions.flatMap(({fn, type}) => toChoices([fn, [type]])),
|
|
||||||
...ctx.genericFunctions.flatMap(({fn, genericType}) => toChoices([fn, [genericType]])),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
if (choice === "(go back)") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const {i, t} = choice;
|
|
||||||
await functionOptions(i, t);
|
|
||||||
return listAllFunctions();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function typeOptions(t, tt) {
|
|
||||||
const choice = await select({
|
|
||||||
message: `actions for type ${prettyT(t)} :: ${prettyT(tt)}`,
|
|
||||||
choices: [
|
|
||||||
"(go back)",
|
|
||||||
"create instance",
|
|
||||||
"list instances",
|
|
||||||
// "list outgoing functions",
|
|
||||||
// "list incoming functions",
|
|
||||||
"print raw",
|
|
||||||
"treat as instance",
|
|
||||||
],
|
|
||||||
});
|
|
||||||
if (choice === "(go back)") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (choice === "create instance") {
|
|
||||||
const i = await createInstance(t);
|
|
||||||
if (i !== undefined) {
|
|
||||||
ctx = ctx.addToCtx({i, t});
|
|
||||||
return instanceOrTypeOrFnOptions({i, t});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (choice === "list instances") {
|
|
||||||
await listInstances(t);
|
|
||||||
}
|
|
||||||
else if (choice === "print raw") {
|
|
||||||
console.log(pretty(t));
|
|
||||||
}
|
|
||||||
else if (choice === "treat as instance") {
|
|
||||||
await instanceOptions(t, tt)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("unimplemented:", choice);
|
|
||||||
}
|
|
||||||
return typeOptions(t, tt);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function listAll() {
|
|
||||||
const choice = await select({
|
|
||||||
message: `all instances:`,
|
|
||||||
choices: [
|
|
||||||
"(go back)",
|
|
||||||
... [...ctx.types.m.keys()].flatMap(i => toChoices([i, ctx.types.getdefault(i)])),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
if (choice === "(go back)") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return instanceOrTypeOrFnOptions(choice);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function instanceOrTypeOrFnOptions({i, t}) {
|
|
||||||
if (t.typeVars) {
|
|
||||||
if (isFunction(t.type)) {
|
|
||||||
return functionOptions(i, t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (isFunction(t)) {
|
|
||||||
return functionOptions(i, t);
|
|
||||||
}
|
|
||||||
if (t === Type || t === GenericType) {
|
|
||||||
return typeOptions(i, t);
|
|
||||||
}
|
|
||||||
return instanceOptions(i,t);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function listInstances(t) {
|
|
||||||
const choice = await select({
|
|
||||||
message: `instances of ${prettyT(t)}:`,
|
|
||||||
choices: [
|
|
||||||
"(go back)",
|
|
||||||
... [...ctx.instances.getdefault(t)].flatMap(i => toChoices([i, [t]])),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
if (choice === "(go back)") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return instanceOrTypeOrFnOptions(choice);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function listTypes(i) {
|
|
||||||
const {strI} = prettyIT({i,t:Type});
|
|
||||||
const choice = await select({
|
|
||||||
message: `type(s) of ${strI}:`,
|
|
||||||
choices: [
|
|
||||||
"(go back)",
|
|
||||||
... [...ctx.types.getdefault(i)].flatMap(t => toChoices([t, ctx.types.getdefault(t)])),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
if (choice === "(go back)") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const {i: chosenType, t: typeOfChosenType} = choice;
|
|
||||||
await typeOptions(chosenType, typeOfChosenType);
|
|
||||||
return listTypes(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function functionOptions(fn, fnT) {
|
|
||||||
const {strI, strT} = prettyIT({i: fn, t: fnT});
|
|
||||||
const choice = await select({
|
|
||||||
message: `actions for function ${strI} :: ${strT}`,
|
|
||||||
choices: [
|
|
||||||
"(go back)",
|
|
||||||
"call",
|
|
||||||
"treat as instance",
|
|
||||||
],
|
|
||||||
});
|
|
||||||
if (choice === "(go back)") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (choice === "call") {
|
|
||||||
await callFunction(fn, fnT);
|
|
||||||
}
|
|
||||||
if (choice === "treat as instance") {
|
|
||||||
await instanceOptions(fn, fnT);
|
|
||||||
}
|
|
||||||
return functionOptions(fn, fnT);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function createInstance(t) {
|
|
||||||
if (t.typeVars && t.typeVars.size === 0) {
|
|
||||||
t = t.type; // can treat as non-generic
|
|
||||||
}
|
|
||||||
if (eqType(t)(Int)) {
|
|
||||||
const n = await number({
|
|
||||||
message: `enter an integer (leave empty to go back):`,
|
|
||||||
step: 1, // only integers
|
|
||||||
});
|
|
||||||
if (n === undefined) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return BigInt(n);
|
|
||||||
}
|
|
||||||
else if (eqType(t)(Double)) {
|
|
||||||
const n = await number({
|
|
||||||
message: `enter a number (leave empty to go back):`,
|
|
||||||
step: 'any',
|
|
||||||
});
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
else if (eqType(t)(UUID)) {
|
|
||||||
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:"});
|
|
||||||
return symbolDescr + '__' + genUUID(16);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
console.log("no prompt handler for creating new", prettyT(t));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function callFunction(fn, fnT) {
|
|
||||||
const {strI, strT} = prettyIT({i: fn, t: fnT});
|
|
||||||
let choices;
|
|
||||||
let inType;
|
|
||||||
if (fnT.typeVars) {
|
|
||||||
// generic
|
|
||||||
choices = [...ctx.types.m.entries()].flatMap(([i, types]) => {
|
|
||||||
return [...types].flatMap(t => {
|
|
||||||
const genT = t.typeVars ? t : makeGeneric(() => t);
|
|
||||||
let assignedFnType;
|
|
||||||
try {
|
|
||||||
assignedFnType = assignFn(fnT, genT);
|
|
||||||
} catch (e) {
|
|
||||||
if (e.message.startsWith("cannot unify")) {
|
|
||||||
// console.warn(e);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
const assignedInType = onlyOccurring(assignedFnType.type.params[0], assignedFnType.typeVars);
|
|
||||||
if (assignedInType.typeVars.size > 0) {
|
|
||||||
return toChoices([i, [assignedInType]]);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return toChoices([i, [assignedInType.type]]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
inType = onlyOccurring(fnT.type.params[0], fnT.typeVars)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
inType = fnT.params[0];
|
|
||||||
choices = [...ctx.instances.getdefault(inType)].flatMap(i => toChoices([i, [inType]]));
|
|
||||||
}
|
|
||||||
|
|
||||||
const choice = await select({
|
|
||||||
message: `select parameter of type ${prettyT(inType)} for function ${strI} :: ${strT}`,
|
|
||||||
choices: [
|
|
||||||
"(go back)",
|
|
||||||
"(new)",
|
|
||||||
...choices,
|
|
||||||
],
|
|
||||||
});
|
|
||||||
let i, t;
|
|
||||||
if (choice === "(go back)") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (choice === "(new)") {
|
|
||||||
i = await createInstance(inType);
|
|
||||||
t = inType;
|
|
||||||
if (i === undefined) {
|
|
||||||
return callFunction(fn, fnT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
i = choice.i;
|
|
||||||
t = choice.t;
|
|
||||||
}
|
|
||||||
const genT = t.typeVars ? t : makeGeneric(() => t);
|
|
||||||
const genFnT = fnT.typeVars ? fnT : makeGeneric(() => fnT);
|
|
||||||
const assignedFnType = assignFn(genFnT, genT);
|
|
||||||
await apply(i, fn, assignedFnType);
|
|
||||||
return callFunction(fn, fnT);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function instanceOptions(i,t) {
|
|
||||||
const {strI, strT} = prettyIT({i,t});
|
|
||||||
const choice = await select({
|
|
||||||
message: `actions for instance ${strI} :: ${strT}`,
|
|
||||||
choices: [
|
|
||||||
"(go back)",
|
|
||||||
"transform",
|
|
||||||
"list type(s)",
|
|
||||||
]
|
|
||||||
})
|
|
||||||
if (choice === "(go back)") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (choice === "transform") {
|
|
||||||
await transform(i, t)
|
|
||||||
}
|
|
||||||
if (choice === "list type(s)") {
|
|
||||||
await listTypes(i);
|
|
||||||
}
|
|
||||||
return await instanceOptions(i,t);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function transform(i, t) {
|
|
||||||
const {strI, strT} = prettyIT({i, t});
|
|
||||||
// console.log(ctx.functionsFrom.getdefault(t));
|
|
||||||
|
|
||||||
const genT = t.typeVars ? t : makeGeneric(() => t);
|
|
||||||
|
|
||||||
const choice = await select({
|
|
||||||
message: `choose transformation to perform on ${strI} :: ${strT}`,
|
|
||||||
choices: [
|
|
||||||
"(go back)",
|
|
||||||
|
|
||||||
...ctx.functions
|
|
||||||
.filter(({type}) => {
|
|
||||||
// console.log(type.params[0], t);
|
|
||||||
return eqType(type.params[0])(t)
|
|
||||||
})
|
|
||||||
.flatMap(({fn, type}) => toChoices([fn, [type]])),
|
|
||||||
|
|
||||||
...ctx.genericFunctions
|
|
||||||
.flatMap(({fn, genericType}) => {
|
|
||||||
let fnType;
|
|
||||||
try {
|
|
||||||
fnType = assignFn(genericType, genT);
|
|
||||||
} catch (e) {
|
|
||||||
if (e.message.startsWith("cannot unify")) {
|
|
||||||
// console.warn(e);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
return toChoices([fn, [fnType]]);
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
});
|
|
||||||
if (choice === "(go back)") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const {i:fn,t:fnT} = choice;
|
|
||||||
await apply(i, fn, fnT);
|
|
||||||
return transform(i, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function apply(i, fn, fnT) {
|
|
||||||
const result = fn(i);
|
|
||||||
let resultType;
|
|
||||||
if (fnT.typeVars) {
|
|
||||||
const res = onlyOccurring(fnT.type.params[1], fnT.typeVars);
|
|
||||||
resultType = res.typeVars.size > 0 ? res : res.type;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
resultType = fnT.params[1];
|
|
||||||
}
|
|
||||||
// update context with newly produced value
|
|
||||||
ctx = ctx
|
|
||||||
.addToCtx({i: result, t: resultType})
|
|
||||||
.addToCtx({i: resultType, t: resultType.typeVars ? GenericType : Type});
|
|
||||||
const {strI: strResult, strT: strResultType} = prettyIT({i: result, t: resultType});
|
|
||||||
console.log(`result = ${strResult} :: ${strResultType}`);
|
|
||||||
return instanceOrTypeOrFnOptions({i: result, t: resultType});
|
|
||||||
}
|
|
||||||
|
|
||||||
topPrompt();
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
|
import { inspect } from "node:util";
|
||||||
import { eqType, getSymbol } from "../primitives/type.js";
|
import { eqType, getSymbol } from "../primitives/type.js";
|
||||||
import { zip } from "../util/util.js";
|
import { zip } from "../util/util.js";
|
||||||
import { pretty, prettyT } from '../util/pretty.js';
|
import { pretty, prettyT } from '../util/pretty.js';
|
||||||
import { isTypeVar, TYPE_VARS } from "../primitives/typevars.js";
|
import { isTypeVar, TYPE_VARS } from "../primitives/typevars.js";
|
||||||
|
import { inspectType } from "../meta/type_constructor.js";
|
||||||
|
|
||||||
// helper for creating generic types
|
// helper for creating generic types
|
||||||
// for instance, the type:
|
// for instance, the type:
|
||||||
|
|
@ -150,6 +152,7 @@ const __unify = (fType, aType, fStack=[], aStack=[]) => {
|
||||||
type: {
|
type: {
|
||||||
symbol: fType.symbol,
|
symbol: fType.symbol,
|
||||||
params: unifiedParams,
|
params: unifiedParams,
|
||||||
|
[inspect.custom]: inspectType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
@ -177,6 +180,7 @@ export const substitute = (type, substitutions, stack=[]) => {
|
||||||
}
|
}
|
||||||
return substitute(param, substitutions, [...stack, parent]);
|
return substitute(param, substitutions, [...stack, parent]);
|
||||||
}),
|
}),
|
||||||
|
[inspect.custom]: inspectType,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,13 @@ import { getHumanReadableName } from "../primitives/symbol.js";
|
||||||
import { inspect } from "util";
|
import { inspect } from "util";
|
||||||
import { prettyT } from "../util/pretty.js";
|
import { prettyT } from "../util/pretty.js";
|
||||||
|
|
||||||
|
export const inspectType = function(depth, options, inspect){ return options.stylize(prettyT(this), 'date'); }
|
||||||
|
|
||||||
const __makeTypeConstructor = (symbol, nAry, params) => {
|
const __makeTypeConstructor = (symbol, nAry, params) => {
|
||||||
if (nAry === 0) {
|
if (nAry === 0) {
|
||||||
return {
|
return {
|
||||||
symbol, params,
|
symbol, params,
|
||||||
[inspect.custom](depth, options, inspect){ return options.stylize(prettyT(this), 'null'); },
|
[inspect.custom]: inspectType,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// only for debugging, do we give the function a name
|
// only for debugging, do we give the function a name
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,6 @@ const mkType = getDefaultTypeParser();
|
||||||
|
|
||||||
export const ModuleDouble = [
|
export const ModuleDouble = [
|
||||||
newDynamic(addDouble)(mkType("Double -> Double -> Double")),
|
newDynamic(addDouble)(mkType("Double -> Double -> Double")),
|
||||||
newDynamic(mulDouble)(mkType("Double -> Double -> Double") ),
|
newDynamic(mulDouble)(mkType("Double -> Double -> Double")),
|
||||||
newDynamic(eqDouble)(mkType("Double -> Double -> Bool") ),
|
newDynamic(eqDouble )(mkType("Double -> Double -> Bool" )),
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { inspect } from "node:util";
|
import { inspect } from "node:util";
|
||||||
import { assignFn } from "../generics/generics.js";
|
import { assignFn } from "../generics/generics.js";
|
||||||
|
import { select } from "@inquirer/prompts";
|
||||||
|
|
||||||
function inspectDynamic(_depth, options, inspect) {
|
function inspectDynamic(_depth, options, inspect) {
|
||||||
return `${inspect(this.i, options)} :: ${inspect(this.t, options)}`;
|
return `${inspect(this.i, options)} :: ${inspect(this.t, options)}`;
|
||||||
|
|
|
||||||
|
|
@ -2,29 +2,29 @@ import { newDynamic } from "./dynamic.js";
|
||||||
import { SymbolInt, UUID, SymbolBool, SymbolDouble, SymbolByte, SymbolChar, SymbolUnit, SymbolBottom, SymbolUUID, SymbolType, SymbolTop, Type, Int, Bool, Double, Byte, Char, Unit, Bottom, Top, SymbolDynamic, Dynamic } from "./primitive_types.js";
|
import { SymbolInt, UUID, SymbolBool, SymbolDouble, SymbolByte, SymbolChar, SymbolUnit, SymbolBottom, SymbolUUID, SymbolType, SymbolTop, Type, Int, Bool, Double, Byte, Char, Unit, Bottom, Top, SymbolDynamic, Dynamic } from "./primitive_types.js";
|
||||||
|
|
||||||
export const ModulePrimitiveSymbols = [
|
export const ModulePrimitiveSymbols = [
|
||||||
newDynamic(SymbolInt )(UUID ),
|
newDynamic(SymbolInt )(UUID),
|
||||||
newDynamic(SymbolBool )(UUID ),
|
newDynamic(SymbolBool )(UUID),
|
||||||
newDynamic(SymbolDouble )(UUID ),
|
newDynamic(SymbolDouble )(UUID),
|
||||||
newDynamic(SymbolByte )(UUID ),
|
newDynamic(SymbolByte )(UUID),
|
||||||
newDynamic(SymbolChar )(UUID ),
|
newDynamic(SymbolChar )(UUID),
|
||||||
newDynamic(SymbolUnit )(UUID ),
|
newDynamic(SymbolUnit )(UUID),
|
||||||
newDynamic(SymbolBottom )(UUID ),
|
newDynamic(SymbolBottom )(UUID),
|
||||||
newDynamic(SymbolUUID )(UUID ),
|
newDynamic(SymbolUUID )(UUID),
|
||||||
newDynamic(SymbolType )(UUID ),
|
newDynamic(SymbolType )(UUID),
|
||||||
newDynamic(SymbolTop )(UUID ),
|
newDynamic(SymbolTop )(UUID),
|
||||||
newDynamic(SymbolDynamic )(UUID ),
|
newDynamic(SymbolDynamic)(UUID),
|
||||||
];
|
];
|
||||||
|
|
||||||
export const ModulePrimitiveTypes = [
|
export const ModulePrimitiveTypes = [
|
||||||
newDynamic(Int )(Type ),
|
newDynamic(Int )(Type),
|
||||||
newDynamic(Bool )(Type ),
|
newDynamic(Bool )(Type),
|
||||||
newDynamic(Double )(Type ),
|
newDynamic(Double )(Type),
|
||||||
newDynamic(Byte )(Type ),
|
newDynamic(Byte )(Type),
|
||||||
newDynamic(Char )(Type ),
|
newDynamic(Char )(Type),
|
||||||
newDynamic(Unit )(Type ),
|
newDynamic(Unit )(Type),
|
||||||
newDynamic(Bottom )(Type ),
|
newDynamic(Bottom )(Type),
|
||||||
newDynamic(UUID )(Type ),
|
newDynamic(UUID )(Type),
|
||||||
newDynamic(Type )(Type ),
|
newDynamic(Type )(Type),
|
||||||
newDynamic(Top )(Type ),
|
newDynamic(Top )(Type),
|
||||||
newDynamic(Dynamic )(Type ),
|
newDynamic(Dynamic)(Type),
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -3,4 +3,4 @@ import { compareTypes } from "../compare/type.js";
|
||||||
export const getSymbol = type => type.symbol;
|
export const getSymbol = type => type.symbol;
|
||||||
export const getParams = type => type.params;
|
export const getParams = type => type.params;
|
||||||
|
|
||||||
export const eqType = t1 => t2 => compareTypes(t1, t2) === 0;
|
export const eqType = t1 => t2 => compareTypes(t1)(t2) === 0;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
import { inspect } from "node:util";
|
export const unit = {};
|
||||||
|
|
||||||
export const unit = {
|
|
||||||
[inspect.custom](depth, options, inspect){ return '()'; }
|
|
||||||
};
|
|
||||||
|
|
||||||
export const eqUnit = _ => _ => true;
|
export const eqUnit = _ => _ => true;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,13 @@ export const set = dict => key => value => new RBTreeWrapper(dict.tree.remove(ke
|
||||||
export const remove = dict => key => new RBTreeWrapper(dict.tree.remove(key), inspectDict);
|
export const remove = dict => key => new RBTreeWrapper(dict.tree.remove(key), inspectDict);
|
||||||
export const length = dict => dict.tree.length;
|
export const length = dict => dict.tree.length;
|
||||||
|
|
||||||
|
export const fold = callback => initial => dict => {
|
||||||
|
let acc = initial;
|
||||||
|
for (const iter=dict.tree.begin; iter !== undefined && iter.valid; iter.next()) {
|
||||||
|
acc = callback(acc)(iter.key)(iter.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const first = dict => dict.tree.begin;
|
export const first = dict => dict.tree.begin;
|
||||||
export const last = dict => dict.tree.end;
|
export const last = dict => dict.tree.end;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { makeTypeParser } from "../parser/type_parser.js";
|
import { makeTypeParser } from "../parser/type_parser.js";
|
||||||
import { makeTypeConstructor } from "../meta/type_constructor.js";
|
import { makeTypeConstructor } from "../meta/type_constructor.js";
|
||||||
import { emptyDict, first, has, last, length, read, remove, set } from "./dict.js";
|
import { emptyDict, first, fold, has, last, length, read, remove, set } from "./dict.js";
|
||||||
import { newDynamic } from "../primitives/dynamic.js";
|
import { newDynamic } from "../primitives/dynamic.js";
|
||||||
|
|
||||||
export const symbolDictIterator = 'DictIterator__d9d175b6bfd1283f00851a99787d0499';
|
export const symbolDictIterator = 'DictIterator__d9d175b6bfd1283f00851a99787d0499';
|
||||||
|
|
@ -13,12 +13,13 @@ const mkType = makeTypeParser({
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ModuleDict = [
|
export const ModuleDict = [
|
||||||
newDynamic(emptyDict )(mkType("(a -> a -> Int) -> (a => b)") ),
|
newDynamic(emptyDict)(mkType("(a -> a -> Int) -> (a => b) ")),
|
||||||
newDynamic(has )(mkType("(a => b) -> a -> Bool")),
|
newDynamic(has )(mkType("(a => b) -> a -> Bool ")),
|
||||||
newDynamic(set )(mkType("(a => b) -> a -> b -> (a => b)")),
|
newDynamic(set )(mkType("(a => b) -> a -> b -> (a => b) ")),
|
||||||
newDynamic(remove )(mkType("(a => b) -> a -> (a => b)")),
|
newDynamic(remove )(mkType("(a => b) -> a -> (a => b) ")),
|
||||||
newDynamic(length )(mkType("(a => b) -> Int")),
|
newDynamic(length )(mkType("(a => b) -> Int ")),
|
||||||
newDynamic(first )(mkType("(a => b) -> (a |=>| b)")),
|
newDynamic(fold )(mkType("(c -> a -> b -> c) -> c -> (a => b) -> c ")),
|
||||||
newDynamic(last )(mkType("(a => b) -> (a |=>| b)")),
|
newDynamic(first )(mkType("(a => b) -> (a |=>| b) ")),
|
||||||
|
newDynamic(last )(mkType("(a => b) -> (a |=>| b) ")),
|
||||||
newDynamic(read )(mkType("(a |=>| b) -> (Unit + ((a*b) * (a |=>| b)))")),
|
newDynamic(read )(mkType("(a |=>| b) -> (Unit + ((a*b) * (a |=>| b)))")),
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ export const push = ls => elem => ls.concat([elem]);
|
||||||
export const pop = ls => ls.pop();
|
export const pop = ls => ls.pop();
|
||||||
export const map = ls => fn => ls.map(elem => fn(elem));
|
export const map = ls => fn => ls.map(elem => fn(elem));
|
||||||
export const length = ls => ls.length;
|
export const length = ls => ls.length;
|
||||||
export const fold = ls => callback => initial => {
|
export const fold = callback => initial => ls => {
|
||||||
let acc = initial;
|
let acc = initial;
|
||||||
for (let i=0; i<ls.length; i++) {
|
for (let i=0; i<ls.length; i++) {
|
||||||
acc = callback(acc)(ls[i]);
|
acc = callback(acc)(ls[i]);
|
||||||
|
|
|
||||||
|
|
@ -12,5 +12,5 @@ export const ModuleList = [
|
||||||
newDynamic(pop )(mkType("[a] -> a")),
|
newDynamic(pop )(mkType("[a] -> a")),
|
||||||
newDynamic(map )(mkType("[a] -> (a -> b) -> [b]")),
|
newDynamic(map )(mkType("[a] -> (a -> b) -> [b]")),
|
||||||
newDynamic(length )(mkType("[a] -> Int")),
|
newDynamic(length )(mkType("[a] -> Int")),
|
||||||
newDynamic(fold )(mkType("[a] -> (b -> a -> b) -> b -> b")),
|
newDynamic(fold )(mkType("(b -> a -> b) -> b -> [a] -> b")),
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,10 @@ export const add = set => key => set.tree.get(key) === true ? set : new RBTreeWr
|
||||||
export const remove = set => key => new RBTreeWrapper(set.tree.remove(key), inspectSet);
|
export const remove = set => key => new RBTreeWrapper(set.tree.remove(key), inspectSet);
|
||||||
export const length = set => set.tree.length;
|
export const length = set => set.tree.length;
|
||||||
|
|
||||||
export const fold = set => callback => initial => {
|
export const fold = callback => initial => set => {
|
||||||
let acc = initial;
|
let acc = initial;
|
||||||
let iter = set.tree.begin;
|
for (const iter=set.tree.begin; iter !== undefined && iter.valid; iter.next()) {
|
||||||
while (iter !== undefined && iter.valid) {
|
acc = callback(acc)(iter.key);
|
||||||
acc = callback(acc, iter.key);
|
|
||||||
}
|
}
|
||||||
return acc;
|
return acc;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,18 @@ export const symbolSetIterator = 'SetIterator__f6b0ddd78ed41c58e5a442f2681da011'
|
||||||
const setIterator = makeTypeConstructor(symbolSetIterator)(1);
|
const setIterator = makeTypeConstructor(symbolSetIterator)(1);
|
||||||
|
|
||||||
const mkType = makeTypeParser({
|
const mkType = makeTypeParser({
|
||||||
|
// extra syntax to represent a set iterator:
|
||||||
extraBracketOperators: [['<', ['>', setIterator]]],
|
extraBracketOperators: [['<', ['>', setIterator]]],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const ModuleSet = [
|
export const ModuleSet = [
|
||||||
newDynamic(emptySet )(mkType("(a -> a -> Int) -> {a}") ),
|
newDynamic(emptySet)(mkType("(a -> a -> Int) -> {a}" )),
|
||||||
newDynamic(has )(mkType("{a} -> a -> Bool")),
|
newDynamic(has )(mkType("{a} -> a -> Bool" )),
|
||||||
newDynamic(add )(mkType("{a} -> a -> {a}")),
|
newDynamic(add )(mkType("{a} -> a -> {a}" )),
|
||||||
newDynamic(remove )(mkType("{a} -> a -> {a}")),
|
newDynamic(remove )(mkType("{a} -> a -> {a}" )),
|
||||||
newDynamic(length )(mkType("{a} -> Int")),
|
newDynamic(length )(mkType("{a} -> Int" )),
|
||||||
newDynamic(fold )(mkType("{a} -> (b -> a -> b) -> b")),
|
newDynamic(fold )(mkType("(b -> a -> b) -> b -> {a} -> b")),
|
||||||
newDynamic(first )(mkType("{a} -> <a>")),
|
newDynamic(first )(mkType("{a} -> <a>" )),
|
||||||
newDynamic(last )(mkType("{a} -> <a>")),
|
newDynamic(last )(mkType("{a} -> <a>" )),
|
||||||
newDynamic(read )(mkType("<a> -> (Unit + (a * <a>))")),
|
newDynamic(read )(mkType("<a> -> (Unit + (a * <a>))" )),
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue