interactive prompt
This commit is contained in:
parent
bc91d9bf39
commit
3596e01c28
9 changed files with 298 additions and 105 deletions
320
main.js
320
main.js
|
|
@ -1,61 +1,33 @@
|
||||||
import { select } from '@inquirer/prompts';
|
import { select } from '@inquirer/prompts';
|
||||||
import { ModulePoint } from "./lib/point.js";
|
import { ModulePoint } from "./lib/point.js";
|
||||||
import { DefaultMap, pretty } from './util.js';
|
import { DefaultMap, pretty, prettyT } from './util.js';
|
||||||
import { symbolFunction } from './structures/types.js';
|
import { symbolFunction } from './structures/types.js';
|
||||||
import { ModuleStd } from './stdlib.js';
|
import { ModuleStd } from './stdlib.js';
|
||||||
|
import { Type } from './type.js';
|
||||||
|
|
||||||
class Context {
|
class Context {
|
||||||
constructor(mod) {
|
constructor(mod) {
|
||||||
this.functionsFrom = new DefaultMap(() => []); // type to outgoing function
|
this.types = new DefaultMap(() => new Set()); // instance to type
|
||||||
this.functionsTo = new DefaultMap(() => []); // type to incoming function
|
this.instances = new DefaultMap(() => new Set()); // type to instance
|
||||||
this.types = new DefaultMap(() => []); // instance to type
|
|
||||||
this.instances = new DefaultMap(() => []); // type to instance
|
this.functionsFrom = new DefaultMap(() => new Set()); // type to outgoing function
|
||||||
|
this.functionsTo = new DefaultMap(() => new Set()); // type to incoming function
|
||||||
|
|
||||||
for (const {i, t} of mod.l) {
|
for (const {i, t} of mod.l) {
|
||||||
this.types.getdefault(i, true).push(t);
|
this.types.getdefault(i, true).add(t);
|
||||||
this.instances.getdefault(t, true).push(i);
|
this.instances.getdefault(t, true).add(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const [i, types] of this.instances.m.entries()) {
|
for (const t of this.instances.m.keys()) {
|
||||||
for (const t of types) {
|
if (t.symbol === symbolFunction) {
|
||||||
if (t.symbol === symbolFunction) {
|
// 't' is a function signature
|
||||||
// 'i' is a function
|
for (const fn of this.instances.getdefault(t)) {
|
||||||
this.functionsFrom.getdefault(t.params[0], true).push(i);
|
this.functionsFrom.getdefault(t.params[0], true).add(fn);
|
||||||
this.functionsFrom.getdefault(t.params[1], true).push(i);
|
this.functionsTo .getdefault(t.params[1], true).add(fn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// const callFunctionOnEveryValue = (fn, fnType, indent=1) => {
|
|
||||||
// const inType = getIn(fnType);
|
|
||||||
// const outType = getOut(fnType);
|
|
||||||
// console.log();
|
|
||||||
// if (fn.name !== "") {
|
|
||||||
// console.log("--".repeat(indent), fn, ':', fnType);
|
|
||||||
// }
|
|
||||||
// for (const i of this.instances.getdefault(inType)) {
|
|
||||||
// try {
|
|
||||||
// const result = fn(i);
|
|
||||||
// console.log("--".repeat(indent+1), i, '->', result);
|
|
||||||
// if (this.types.getdefault(outType).includes(Function)) {
|
|
||||||
// if (indent < 5) {
|
|
||||||
// callFunctionOnEveryValue(result, outType, indent+2);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } catch (e) {
|
|
||||||
// console.log("--".repeat(indent+1), i, `-> (exception: ${e})`);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// for (const fntypes of this.functionsFrom.m.values()) {
|
|
||||||
// for (const fntype of fntypes) {
|
|
||||||
// for (const fn of this.instances.getdefault(fntype)) {
|
|
||||||
// callFunctionOnEveryValue(fn, fntype);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const ctx = new Context({l:[
|
const ctx = new Context({l:[
|
||||||
|
|
@ -63,49 +35,231 @@ const ctx = new Context({l:[
|
||||||
...ModulePoint.l,
|
...ModulePoint.l,
|
||||||
]});
|
]});
|
||||||
|
|
||||||
const makeChoice = ([i, t]) => {
|
|
||||||
return {
|
|
||||||
value: {i, t: t[0]},
|
const prettyIT = ({i, t}) => ({
|
||||||
name: pretty(i),
|
strI: isType(i) ? prettyT(i) : pretty(i),
|
||||||
description: ` :: ${pretty(t[0])}`,
|
strT: prettyT(t),
|
||||||
short: `${pretty(i)} :: ${pretty(t[0])}`,
|
})
|
||||||
};
|
|
||||||
|
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}`,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let {i,t} = await select({
|
const isType = i => ctx.types.getdefault(i).has(Type);
|
||||||
message: "Choose value:",
|
|
||||||
choices: [...ctx.types.entries()].map(makeChoice),
|
|
||||||
});
|
|
||||||
|
|
||||||
while (true) {
|
async function topPrompt() {
|
||||||
let fn, fnType;
|
const action = await select({
|
||||||
if (ctx.types.getdefault(t).includes(Function)) {
|
message: "What do you want to do?",
|
||||||
fn = i;
|
choices: [
|
||||||
fnType = t;
|
"list all types",
|
||||||
const s = await select({
|
"list all functions",
|
||||||
message: "Select input for function:",
|
"list all",
|
||||||
choices: ctx.instances.getdefault(getIn(t))
|
],
|
||||||
.map(i => [i, ctx.types.getdefault(i)])
|
});
|
||||||
.map(makeChoice),
|
if (action === "list all types") {
|
||||||
});
|
await listAllTypes();
|
||||||
i = s.i;
|
|
||||||
t = s.t;
|
|
||||||
}
|
}
|
||||||
else{
|
if (action === "list all") {
|
||||||
fn = await select({
|
await listAllInstances();
|
||||||
message: "Choose function:",
|
|
||||||
choices: [...ctx.functionsFrom.getdefault(t).map(fn => ({
|
|
||||||
value: fn,
|
|
||||||
name: pretty(fn),
|
|
||||||
description: `${pretty(fn)} :: ${pretty(ctx.types.getdefault(fn)[0])}`,
|
|
||||||
short: `${pretty(fn)} :: ${pretty(ctx.types.getdefault(fn)[0])}`,
|
|
||||||
})
|
|
||||||
)],
|
|
||||||
});
|
|
||||||
fnType = ctx.types.getdefault(fn)[0];
|
|
||||||
}
|
}
|
||||||
|
return topPrompt();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listAllTypes() {
|
||||||
|
const choice = await select({
|
||||||
|
message: "select type:",
|
||||||
|
choices: [
|
||||||
|
"(go back)",
|
||||||
|
...[...ctx.instances.m.keys()].map(t => ({
|
||||||
|
value: t,
|
||||||
|
name: prettyT(t),
|
||||||
|
})),
|
||||||
|
]
|
||||||
|
});
|
||||||
|
if (choice === "(go back)") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await typeOptions(choice);
|
||||||
|
return listAllTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function typeOptions(t) {
|
||||||
|
const choice = await select({
|
||||||
|
message: `actions for type ${prettyT(t)} :: Type`,
|
||||||
|
choices: [
|
||||||
|
"(go back)",
|
||||||
|
"list instances",
|
||||||
|
"list outgoing functions",
|
||||||
|
"list incoming functions",
|
||||||
|
"treat as instance",
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (choice === "(go back)") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (choice === "list instances") {
|
||||||
|
await listInstances(t);
|
||||||
|
}
|
||||||
|
else if (choice === "treat as instance") {
|
||||||
|
await instanceOptions(t, Type)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("unimplemented:", choice);
|
||||||
|
}
|
||||||
|
return typeOptions(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listAllInstances() {
|
||||||
|
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.symbol === symbolFunction) {
|
||||||
|
return functionOptions(i, t);
|
||||||
|
}
|
||||||
|
if (isType(i)) {
|
||||||
|
return typeOptions(i);
|
||||||
|
}
|
||||||
|
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} = choice;
|
||||||
|
await typeOptions(chosenType);
|
||||||
|
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 callFunction(fn, fnT) {
|
||||||
|
const {strI, strT} = prettyIT({i: fn, t: fnT});
|
||||||
|
const inType = fnT.params[0];
|
||||||
|
const choice = await 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);
|
||||||
|
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 choice = await select({
|
||||||
|
message: `choose transformation to perform on ${strI} :: ${strT}`,
|
||||||
|
choices: [
|
||||||
|
"(go back)",
|
||||||
|
... [...ctx.functionsFrom.getdefault(t)].flatMap(fn => toChoices([fn, ctx.types.getdefault(fn)])),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
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);
|
const result = fn(i);
|
||||||
t = getOut(fnType);
|
const resultType = fnT.params[1];
|
||||||
console.log("result =", result, "::", t);
|
const {strI: strResult, strT: strResultType} = prettyIT({i: result, t: resultType});
|
||||||
i = result;
|
console.log(`result = ${strResult} :: ${strResultType}`);
|
||||||
|
return instanceOrTypeOrFnOptions({i: result, t: resultType});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
topPrompt();
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,10 @@ const eq = x => y => x === y;
|
||||||
export const ModuleChar = {l:[
|
export const ModuleChar = {l:[
|
||||||
{i: Char, t: Type},
|
{i: Char, t: Type},
|
||||||
|
|
||||||
...typedFnType(eq, fnType => fnType({in: Char, out: fnType({in: Char, out: Bool})})),
|
...typedFnType(eq, fnType =>
|
||||||
|
fnType
|
||||||
|
(Char)
|
||||||
|
(fnType
|
||||||
|
(Char)
|
||||||
|
(Bool))),
|
||||||
]};
|
]};
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
// 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
|
||||||
|
|
||||||
|
|
||||||
export const Int = { symbol: Symbol('Int') , params: [] };
|
export const Int = { symbol: Symbol('Int') , params: [] };
|
||||||
export const Bool = { symbol: Symbol('Bool') , params: [] };
|
export const Bool = { symbol: Symbol('Bool') , params: [] };
|
||||||
export const Double = { symbol: Symbol('Double'), params: [] };
|
export const Double = { symbol: Symbol('Double'), params: [] };
|
||||||
export const Byte = { symbol: Symbol('Byte') , params: [] };
|
export const Byte = { symbol: Symbol('Byte') , params: [] };
|
||||||
export const Char = { symbol: Symbol('Char') , params: [] };// Wrapper around function below.
|
export const Char = { symbol: Symbol('Char') , params: [] };
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { Type } from "../type.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
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// to break up dependency cycles, type constructors are defined in their own JS module
|
// to break up dependency cycles, type constructors are defined in their own JS module
|
||||||
|
|
||||||
|
import { Type } from "../type.js";
|
||||||
import { DefaultMap } from "../util.js";
|
import { DefaultMap } from "../util.js";
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -36,14 +37,14 @@ export const typedFnType2 = callback => {
|
||||||
const t = callback(wrappedFnType); // force evaluation
|
const t = callback(wrappedFnType); // force evaluation
|
||||||
return [
|
return [
|
||||||
t,
|
t,
|
||||||
fnTs.map(fnT => ({ i: fnT, t: Function })),
|
fnTs.map(fnT => ({ i: fnT, t: Type })),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Sum type
|
// Sum type
|
||||||
|
|
||||||
const symbolSum = Symbol("Sum");
|
export const symbolSum = Symbol("Sum");
|
||||||
const sumTypeRegistry = new DefaultMap(leftType => new DefaultMap(rightType => ({
|
const sumTypeRegistry = new DefaultMap(leftType => new DefaultMap(rightType => ({
|
||||||
symbol: symbolSum,
|
symbol: symbolSum,
|
||||||
params: [leftType, rightType],
|
params: [leftType, rightType],
|
||||||
|
|
@ -54,7 +55,7 @@ export const sumType = leftType => rightType => sumTypeRegistry.getdefault(leftT
|
||||||
|
|
||||||
// Product type
|
// Product type
|
||||||
|
|
||||||
const symbolProduct = Symbol("Product");
|
export const symbolProduct = Symbol("Product");
|
||||||
const productTypeRegistry = new DefaultMap(leftType => new DefaultMap(rightType => ({
|
const productTypeRegistry = new DefaultMap(leftType => new DefaultMap(rightType => ({
|
||||||
symbol: symbolProduct,
|
symbol: symbolProduct,
|
||||||
params: [leftType, rightType],
|
params: [leftType, rightType],
|
||||||
|
|
@ -65,7 +66,7 @@ export const prodType = leftType => rightType => productTypeRegistry.getdefault(
|
||||||
|
|
||||||
// List type
|
// List type
|
||||||
|
|
||||||
const symbolList = Symbol('List');
|
export const symbolList = Symbol('List');
|
||||||
const listTypeRegistry = new DefaultMap(elementType => ({
|
const listTypeRegistry = new DefaultMap(elementType => ({
|
||||||
symbol: symbolList,
|
symbol: symbolList,
|
||||||
params: [elementType],
|
params: [elementType],
|
||||||
|
|
|
||||||
|
|
@ -8,19 +8,21 @@ import { eqDictType } from "./eq_type";
|
||||||
export const getEq = numDict => numDict.eq;
|
export const getEq = numDict => numDict.eq;
|
||||||
|
|
||||||
export const ModuleEq = {l:[
|
export const ModuleEq = {l:[
|
||||||
...typedFnType(eqDictType, fnType => fnType({in: Type, out: Type})),
|
// type constructor: Type -> Type
|
||||||
|
...typedFnType(eqDictType, fnType => fnType(Type)(Type)),
|
||||||
|
|
||||||
...typedFnType(getEq, fnType => makeGeneric(a =>
|
// (EqDict a) -> a -> a -> Bool
|
||||||
fnType({
|
...typedFnType(getEq, fnType =>
|
||||||
in: eqDictType(a),
|
makeGeneric(a =>
|
||||||
out: fnType({
|
fnType
|
||||||
in: a,
|
(eqDictType(a))
|
||||||
out: fnType({
|
(fnType
|
||||||
in: a,
|
(a)
|
||||||
out: Bool,
|
(fnType
|
||||||
}),
|
(a)
|
||||||
}),
|
(Bool)
|
||||||
}))),
|
)
|
||||||
|
))),
|
||||||
]};
|
]};
|
||||||
|
|
||||||
// all our data (and types) are encoded such that we can test equality the same way:
|
// all our data (and types) are encoded such that we can test equality the same way:
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
import { DefaultMap } from "../util.js";
|
import { DefaultMap } from "../util.js";
|
||||||
|
|
||||||
const eqDictTypeRegistry = new DefaultMap(a => ({ eqDict: a }));
|
const eqDictSymbol = Symbol('EqDict');
|
||||||
|
const eqDictTypeRegistry = new DefaultMap(a => ({
|
||||||
|
symbol: eqDictSymbol,
|
||||||
|
params: [a],
|
||||||
|
}));
|
||||||
export const eqDictType = a => eqDictTypeRegistry.getdefault(a, true);
|
export const eqDictType = a => eqDictTypeRegistry.getdefault(a, true);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
import { DefaultMap } from "../util.js";
|
import { DefaultMap } from "../util.js";
|
||||||
|
|
||||||
const numDictTypeRegistry = new DefaultMap(a => ({ numDict: a }));
|
const numDictSymbol = Symbol("NumDict");
|
||||||
|
const numDictTypeRegistry = new DefaultMap(a => ({
|
||||||
|
symbol: numDictSymbol,
|
||||||
|
params: [a],
|
||||||
|
}));
|
||||||
export const numDictType = a => numDictTypeRegistry.getdefault(a, true);
|
export const numDictType = a => numDictTypeRegistry.getdefault(a, true);
|
||||||
|
|
|
||||||
25
util.js
25
util.js
|
|
@ -47,7 +47,30 @@ export class DefaultMap {
|
||||||
|
|
||||||
|
|
||||||
import { inspect } from 'node:util';
|
import { inspect } from 'node:util';
|
||||||
|
import { symbolFunction, symbolList, symbolProduct, symbolSum } from './structures/types.js';
|
||||||
|
|
||||||
export function pretty(obj) {
|
export function pretty(obj) {
|
||||||
return inspect(obj, {colors: true, depth: null});
|
return inspect(obj, {colors: true, depth: null});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function prettyT(type) {
|
||||||
|
if (type.typeVars) {
|
||||||
|
return `∀${[...type.typeVars].map(prettyT).join(", ")}: ${prettyT(type.type)}`;
|
||||||
|
}
|
||||||
|
if (type.symbol === symbolFunction) {
|
||||||
|
return `${prettyT(type.params[0])} -> ${prettyT(type.params[1])}`;
|
||||||
|
}
|
||||||
|
if (type.symbol === symbolList) {
|
||||||
|
return `[${prettyT(type.params[0])}]`;
|
||||||
|
}
|
||||||
|
if (type.symbol === symbolProduct) {
|
||||||
|
return `(${prettyT(type.params[0])}, ${prettyT(type.params[1])})`;
|
||||||
|
}
|
||||||
|
if (type.symbol === symbolSum) {
|
||||||
|
return `(${prettyT(type.params[0])} | ${prettyT(type.params[1])})`;
|
||||||
|
}
|
||||||
|
if (type.params.length === 0) {
|
||||||
|
return type.symbol.description;
|
||||||
|
}
|
||||||
|
return `${type.symbol.description}(${type.params.map(prettyT).join(", ")})`;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue