interactive prompt can handle polymorphic types
This commit is contained in:
parent
a0e3aa0cb3
commit
4a4983f693
20 changed files with 485 additions and 276 deletions
|
|
@ -1,16 +1,19 @@
|
|||
import { typedFnType } from "./types.js";
|
||||
import { Char, Type } from "../primitives/types.js";
|
||||
import { Char, GenericType, Type } from "../primitives/types.js";
|
||||
import { Int } from "../primitives/types.js";
|
||||
import { makeGeneric } from "../generics/generics.js";
|
||||
import { lsType } from "./types.js";
|
||||
import { Typed } from "../typed.js"
|
||||
|
||||
// 'normal' implementation
|
||||
const emptyList = {l:[]};
|
||||
const emptyListType = makeGeneric(a => lsType(a));
|
||||
const get = ls => i => ls.l[i];
|
||||
const put = ls => i => elem => ({l: ls.l.with(Number(i), elem)});
|
||||
const push = ls => elem => ({l:ls.l.concat([elem])});
|
||||
|
||||
export const String = lsType(Char); // alias
|
||||
export const Module = lsType(Typed);
|
||||
|
||||
export const ModuleList = {l:[
|
||||
// Type -> Type
|
||||
|
|
@ -21,7 +24,8 @@ export const ModuleList = {l:[
|
|||
),
|
||||
|
||||
// [a]
|
||||
{i: emptyList, t: makeGeneric(a => lsType(a))},
|
||||
{i: emptyList, t: emptyListType},
|
||||
{i: emptyListType, t: GenericType},
|
||||
|
||||
// [a] -> Int -> a
|
||||
...typedFnType(get, fnType =>
|
||||
|
|
@ -31,7 +35,7 @@ export const ModuleList = {l:[
|
|||
/* out */ (fnType
|
||||
/* in */ (Int)
|
||||
/* out */ (a)
|
||||
))),
|
||||
)), GenericType),
|
||||
|
||||
// [a] -> Int -> a -> [a]
|
||||
...typedFnType(put, fnType =>
|
||||
|
|
@ -44,7 +48,7 @@ export const ModuleList = {l:[
|
|||
/* in */ (a)
|
||||
/* out */ (lsType(a))
|
||||
)
|
||||
))),
|
||||
)), GenericType),
|
||||
|
||||
// [a] -> a -> [a]
|
||||
...typedFnType(push, fnType =>
|
||||
|
|
@ -55,8 +59,5 @@ export const ModuleList = {l:[
|
|||
(a)
|
||||
(lsType(a))
|
||||
)
|
||||
)
|
||||
),
|
||||
|
||||
// {i: String, t: Type}, // alias
|
||||
), GenericType),
|
||||
]};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,76 @@
|
|||
import { Any } from "../typed.js";
|
||||
import { String } from "./list.js";
|
||||
import { sumType, prodType, fnType } from "./types.js";
|
||||
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 createNominalADT = symbol => variants => {
|
||||
makeTypeConstructor(symbol, 0, )
|
||||
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 createNominalADTFnType =
|
||||
fnType
|
||||
(Any)
|
||||
();
|
||||
export const createNominalADTModuleFnType =
|
||||
fnType(SymbolT)
|
||||
(fnType(lsType(prodType(String)(Type)))
|
||||
(Module));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { makeGeneric } from "../generics/generics.js";
|
||||
import { Type } from "../primitives/types.js";
|
||||
import { GenericType, Type } from "../primitives/types.js";
|
||||
import { typedFnType } from "./types.js";
|
||||
import { prodType } from "./types.js";
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ export const ModuleProduct = {l: [
|
|||
(b)
|
||||
(prodType(a)(b))
|
||||
)
|
||||
)),
|
||||
), GenericType),
|
||||
|
||||
// (a, b) -> a
|
||||
...typedFnType(getLeft, fnType =>
|
||||
|
|
@ -37,7 +37,7 @@ export const ModuleProduct = {l: [
|
|||
fnType
|
||||
(prodType(a)(b))
|
||||
(a)
|
||||
)),
|
||||
), GenericType),
|
||||
|
||||
// (a, b) -> b
|
||||
...typedFnType(getRight, fnType =>
|
||||
|
|
@ -45,5 +45,5 @@ export const ModuleProduct = {l: [
|
|||
fnType
|
||||
(prodType(a)(b))
|
||||
(b)
|
||||
)),
|
||||
), GenericType),
|
||||
]};
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
import { setType, typedFnType } from "./types.js";
|
||||
import { Bool, Type } from "../primitives/types.js";
|
||||
import { Bool, GenericType, Type } from "../primitives/types.js";
|
||||
import { makeGeneric } from "../generics/generics.js";
|
||||
|
||||
// 'normal' implementation
|
||||
const emptySet = new Set();
|
||||
const emptySetType = makeGeneric(a => setType(a));
|
||||
const has = set => elem => set.has(elem);
|
||||
const add = set => elem => new Set([...set, elem]);
|
||||
|
||||
export const ModuleList = {l:[
|
||||
export const ModuleSet = {l:[
|
||||
// Type -> Type
|
||||
...typedFnType(setType, fnType =>
|
||||
fnType
|
||||
|
|
@ -15,7 +16,8 @@ export const ModuleList = {l:[
|
|||
/* out */ (Type)
|
||||
),
|
||||
|
||||
{i: emptySet, t: makeGeneric(a => setType(a))},
|
||||
{i: emptySet , t: emptySetType},
|
||||
{i: emptySetType, t: GenericType },
|
||||
|
||||
...typedFnType(has, fnType =>
|
||||
makeGeneric(a =>
|
||||
|
|
@ -24,7 +26,7 @@ export const ModuleList = {l:[
|
|||
/* out */ (fnType
|
||||
/* in */ (a)
|
||||
/* out */ (Bool)
|
||||
))),
|
||||
)), GenericType),
|
||||
|
||||
...typedFnType(add, fnType =>
|
||||
makeGeneric(a =>
|
||||
|
|
@ -33,6 +35,5 @@ export const ModuleList = {l:[
|
|||
/* out */ (fnType
|
||||
/* in */ (a)
|
||||
/* out */ (setType(a))
|
||||
))),
|
||||
|
||||
)), GenericType),
|
||||
]};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { prodType } from "./types.js";
|
||||
import { Type } from "../primitives/types.js";
|
||||
import { GenericType, Type } from "../primitives/types.js";
|
||||
import { typedFnType } from "./types.js";
|
||||
import { makeGeneric } from "../generics/generics.js";
|
||||
import { sumType } from "./types.js";
|
||||
|
|
@ -31,7 +31,7 @@ export const ModuleSum = {l:[
|
|||
fnType
|
||||
(a)
|
||||
(sumType(a)(b))
|
||||
)),
|
||||
), GenericType),
|
||||
|
||||
// b -> a | b
|
||||
...typedFnType(constructorRight, fnType =>
|
||||
|
|
@ -39,7 +39,7 @@ export const ModuleSum = {l:[
|
|||
fnType
|
||||
(b)
|
||||
(sumType(a)(b))
|
||||
)),
|
||||
), GenericType),
|
||||
|
||||
// a | b -> (a -> c, b-> c) -> c
|
||||
...typedFnType(match, fnType =>
|
||||
|
|
@ -53,5 +53,5 @@ export const ModuleSum = {l:[
|
|||
)
|
||||
(c)
|
||||
)
|
||||
)),
|
||||
), GenericType),
|
||||
]};
|
||||
|
|
|
|||
|
|
@ -13,17 +13,8 @@ export const fnType = makeTypeConstructor(symbolFunction)(2);
|
|||
|
||||
export const isFunction = type => getSymbol(type) === symbolFunction;
|
||||
|
||||
// Convenience function. Wrapper around function below.
|
||||
export const typedFnType = (instance, callback) => {
|
||||
const [t, typesOfFns] = typedFnType2(callback);
|
||||
const res = [
|
||||
{ i: instance, t },
|
||||
...typesOfFns,
|
||||
];
|
||||
return res;
|
||||
};
|
||||
// Convenience function. Creates a function type, and also create Type-links for the function type (being typed by Function) and for all the nested function types. Saves a lot of code writing.
|
||||
export const typedFnType2 = callback => {
|
||||
export const typedFnType = (instance, callback, typeOfType = Type) => {
|
||||
const fnTs = [];
|
||||
const wrappedFnType = inType => outType => {
|
||||
const fnT = fnType(inType)(outType);
|
||||
|
|
@ -31,10 +22,15 @@ export const typedFnType2 = callback => {
|
|||
return fnT;
|
||||
};
|
||||
const t = callback(wrappedFnType); // force evaluation
|
||||
return [
|
||||
t,
|
||||
fnTs.map(fnT => ({ i: fnT, t: Type })),
|
||||
if (t.typeVars && typeOfType === Type) {
|
||||
throw new Error("you probably meant to create a GenericType");
|
||||
}
|
||||
const res = [
|
||||
{ i: instance, t },
|
||||
{ i: t , t: typeOfType },
|
||||
// ...fnTs.map(fnT => ({ i: fnT, t: Type })),
|
||||
];
|
||||
return res;
|
||||
};
|
||||
|
||||
// Sum type
|
||||
|
|
@ -57,14 +53,20 @@ export const lsType = makeTypeConstructor(symbolList)(1);
|
|||
const symbolSet = Symbol('Set');
|
||||
export const setType = makeTypeConstructor(symbolSet)(1);
|
||||
|
||||
|
||||
// Pretty print type
|
||||
export function prettyT(type) {
|
||||
// console.log("pretty:", type);
|
||||
if (typeof type === "symbol") {
|
||||
return type.description;
|
||||
}
|
||||
if (type.typeVars) {
|
||||
if (type.typeVars.size > 0) {
|
||||
return `∀${[...type.typeVars].map(prettyT).join(", ")}: ${prettyT(type.type)}`;
|
||||
return `∀(${[...type.typeVars].map(prettyT).join(", ")}): ${prettyT(type.type)}`;
|
||||
}
|
||||
else {
|
||||
return prettyT(type.type);
|
||||
}
|
||||
return prettyT(type.type);
|
||||
}
|
||||
if (type.symbol === symbolFunction) {
|
||||
return `${prettyT(type.params[0])} -> ${prettyT(type.params[1])}`;
|
||||
|
|
|
|||
|
|
@ -9,17 +9,22 @@ const symbolVersioned = Symbol("Versioned");
|
|||
export const versionedType = makeTypeConstructor(symbolVersioned)(1);
|
||||
|
||||
|
||||
export const constructor = parents => value => {
|
||||
export const constructor = parents => alternatives => {
|
||||
return { parents, alternatives };
|
||||
}
|
||||
|
||||
const constructorType = makeGeneric(a =>
|
||||
fnType
|
||||
(a)
|
||||
(setType(versionedType(a)))
|
||||
(fnType
|
||||
(setType(a))
|
||||
(versionedType(a))
|
||||
)
|
||||
);
|
||||
|
||||
// const getValue = v =>
|
||||
const initial = x => ({ parents: new Set(), alternatives: new Set(x) });
|
||||
|
||||
const initialFnType = makeGeneric(a => fnType(a)(versionedType(a)));
|
||||
|
||||
const eq = eqDict => vA => vB => {
|
||||
return getEq(eqDict)(vA.value,vB.value) // compare values
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue