progress
This commit is contained in:
parent
6af72e525c
commit
145835ad5d
22 changed files with 153 additions and 90 deletions
|
|
@ -8,7 +8,8 @@ import { pretty, zip } from "../util.js";
|
||||||
// makeGeneric(a => fnType({in: a, out: fnType({in: a, out: Bool})}))
|
// makeGeneric(a => fnType({in: a, out: fnType({in: a, out: Bool})}))
|
||||||
export const makeGeneric = callback => {
|
export const makeGeneric = callback => {
|
||||||
// type variables to make available:
|
// type variables to make available:
|
||||||
const typeVars = ['a', 'b', 'c', 'd', 'e'].map(letter => ({
|
const typeVars = ['a', 'b', 'c', 'd', 'e'].map(
|
||||||
|
letter => ({
|
||||||
symbol: Symbol(letter),
|
symbol: Symbol(letter),
|
||||||
params: [],
|
params: [],
|
||||||
}));
|
}));
|
||||||
|
|
@ -28,6 +29,7 @@ export const occurring = (type, typeVars) => {
|
||||||
return new Set(type.params.flatMap(p => [...occurring(p, typeVars)]));
|
return new Set(type.params.flatMap(p => [...occurring(p, typeVars)]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge 2 substitution-mappings, uni-directional.
|
||||||
const mergeOneWay = (m1, m2) => {
|
const mergeOneWay = (m1, m2) => {
|
||||||
const m1copy = new Map(m1);
|
const m1copy = new Map(m1);
|
||||||
const m2copy = new Map(m2);
|
const m2copy = new Map(m2);
|
||||||
|
|
@ -41,6 +43,7 @@ const mergeOneWay = (m1, m2) => {
|
||||||
return [true, m1copy, m2copy, new Set()]; // stable
|
return [true, m1copy, m2copy, new Set()]; // stable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Merge 2 substitution-mappings, bi-directional.
|
||||||
export const mergeTwoWay = (m1, m2) => {
|
export const mergeTwoWay = (m1, m2) => {
|
||||||
// check for conflicts:
|
// check for conflicts:
|
||||||
for (const [typeVar, actual] of m1) {
|
for (const [typeVar, actual] of m1) {
|
||||||
|
|
@ -69,10 +72,12 @@ export const unify = (
|
||||||
{typeVars: formalTypeVars, type: formalType},
|
{typeVars: formalTypeVars, type: formalType},
|
||||||
{typeVars: actualTypeVars, type: actualType},
|
{typeVars: actualTypeVars, type: actualType},
|
||||||
) => {
|
) => {
|
||||||
// console.log("unify", {formalTypeVars, formalType, actualTypeVars, actualType});
|
// console.log("unify", pretty({formalTypeVars, formalType, actualTypeVars, actualType}));
|
||||||
|
|
||||||
if (formalTypeVars.has(formalType)) {
|
if (formalTypeVars.has(formalType)) {
|
||||||
// simplest case: formalType is a type paramater
|
// simplest case: formalType is a type paramater
|
||||||
// => substitute with actualType
|
// => substitute with actualType
|
||||||
|
// console.log("assign actual to formal");
|
||||||
return {
|
return {
|
||||||
substitutions: new Map([[formalType, actualType]]),
|
substitutions: new Map([[formalType, actualType]]),
|
||||||
typeVars: new Set([
|
typeVars: new Set([
|
||||||
|
|
@ -84,6 +89,7 @@ export const unify = (
|
||||||
}
|
}
|
||||||
if (actualTypeVars.has(actualType)) {
|
if (actualTypeVars.has(actualType)) {
|
||||||
// same as above, but in the other direction
|
// same as above, but in the other direction
|
||||||
|
// console.log("assign formal to actual");
|
||||||
return {
|
return {
|
||||||
substitutions: new Map([[actualType, formalType]]),
|
substitutions: new Map([[actualType, formalType]]),
|
||||||
typeVars: new Set([
|
typeVars: new Set([
|
||||||
|
|
@ -98,6 +104,7 @@ export const unify = (
|
||||||
throw new Error(`cannot unify ${pretty(formalType.symbol)} and ${pretty(actualType.symbol)}`);
|
throw new Error(`cannot unify ${pretty(formalType.symbol)} and ${pretty(actualType.symbol)}`);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// console.log("symbols match - unify recursively", formalType.symbol);
|
||||||
const unifiedParams = zip(formalType.params, actualType.params).map(([formalParam, actualParam]) => unify({typeVars: formalTypeVars, type: formalParam}, {typeVars: actualTypeVars, type: actualParam}));
|
const unifiedParams = zip(formalType.params, actualType.params).map(([formalParam, actualParam]) => unify({typeVars: formalTypeVars, type: formalParam}, {typeVars: actualTypeVars, type: actualParam}));
|
||||||
const [unifiedSubstitusions, deleted] = unifiedParams.reduce(([substitutionsSoFar, deletedSoFar], cur) => {
|
const [unifiedSubstitusions, deleted] = unifiedParams.reduce(([substitutionsSoFar, deletedSoFar], cur) => {
|
||||||
// console.log('merging', substitutionsSoFar, cur.substitutions);
|
// console.log('merging', substitutionsSoFar, cur.substitutions);
|
||||||
|
|
@ -124,6 +131,12 @@ export const substitute = (type, substitutions) => {
|
||||||
// type IS a type var to be substituted:
|
// type IS a type var to be substituted:
|
||||||
return substitutions.get(type);
|
return substitutions.get(type);
|
||||||
}
|
}
|
||||||
|
if (type.params.length === 0) {
|
||||||
|
// Attention: there's a reason why we have this special case.
|
||||||
|
// Types are compared by object ID, so we don't want to create a new object for a type that takes no type parameters (then the newly create type would differ).
|
||||||
|
// Should fix this some day.
|
||||||
|
return type;
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
symbol: type.symbol,
|
symbol: type.symbol,
|
||||||
params: type.params.map(p => substitute(p, substitutions)),
|
params: type.params.map(p => substitute(p, substitutions)),
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,40 @@
|
||||||
import { Bool, Int } from "../primitives/types.js";
|
import { Bool, Int } from "../primitives/types.js";
|
||||||
import { lsType } from "../structures/types.js";
|
import { fnType, lsType } from "../structures/types.js";
|
||||||
import { fnType } from "../structures/function.js";
|
|
||||||
import { assign, makeGeneric, unify } from "./generics.js";
|
import { assign, makeGeneric, unify } from "./generics.js";
|
||||||
import { pretty } from "../util.js";
|
import { pretty } from "../util.js";
|
||||||
|
|
||||||
// a -> Int
|
// a -> Int
|
||||||
const a_to_Int = makeGeneric(a => fnType({in: a, out: Int}));
|
const a_to_Int = makeGeneric(a => fnType(a)(Int));
|
||||||
// Bool -> Int
|
// Bool -> Int
|
||||||
const Bool_to_Int = makeGeneric(() => fnType({in: lsType(Bool), out: Int}));
|
const Bool_to_Int = makeGeneric(() => fnType(lsType(Bool))(Int));
|
||||||
console.log("should be: [Bool] -> Int")
|
console.log("should be: [Bool] -> Int")
|
||||||
console.log(pretty(unify(a_to_Int, Bool_to_Int)));
|
console.log(pretty(unify(a_to_Int, Bool_to_Int)));
|
||||||
|
|
||||||
// (a -> a) -> b
|
// (a -> a) -> b
|
||||||
const fnType2 = makeGeneric((a,b) => fnType({in: fnType({in: a, out: a}), out: b}));
|
const fnType2 = makeGeneric((a,b) => fnType(fnType(a)(a))(b));
|
||||||
// (Bool -> Bool) -> a
|
// (Bool -> Bool) -> a
|
||||||
const fnType3 = makeGeneric(a => fnType({in: fnType({in: Bool, out: Bool}), out: a}));
|
const fnType3 = makeGeneric(a => fnType(fnType(Bool)(Bool))(a));
|
||||||
console.log("should be: (Bool -> Bool) -> a");
|
console.log("should be: (Bool -> Bool) -> a");
|
||||||
console.log(pretty(unify(fnType2, fnType3)));
|
console.log(pretty(unify(fnType2, fnType3)));
|
||||||
|
|
||||||
// (a -> b) -> [a] -> [b]
|
// (a -> b) -> [a] -> [b]
|
||||||
const mapFnType = makeGeneric((a,b) =>
|
const mapFnType = makeGeneric((a,b) =>
|
||||||
fnType({
|
fnType
|
||||||
in: fnType({in: a, out: b}),
|
(fnType(a)(b))
|
||||||
out: fnType({in: lsType(a), out: lsType(b)}),
|
(fnType(lsType(a))(lsType(b))))
|
||||||
}));
|
|
||||||
// a -> a
|
// a -> a
|
||||||
const idFnType = makeGeneric(a =>
|
const idFnType = makeGeneric(a =>
|
||||||
fnType({in: a, out: a}));
|
fnType(a)(a));
|
||||||
console.log("should be: [a] -> [a]");
|
console.log("should be: [a] -> [a]");
|
||||||
console.log(pretty(assign(mapFnType, idFnType)));
|
console.log(pretty(assign(mapFnType, idFnType)));
|
||||||
|
|
||||||
// (a -> Int) -> [a] -> a
|
// (a -> Int) -> [a] -> a
|
||||||
const weirdFnType = makeGeneric(a =>
|
const weirdFnType = makeGeneric(a =>
|
||||||
fnType({
|
fnType
|
||||||
in: fnType({in: a, out: Int}),
|
(fnType(a)(Int))
|
||||||
out: fnType({in: lsType(a), out: a}),
|
(fnType
|
||||||
}));
|
(lsType(a))
|
||||||
|
(a)))
|
||||||
// we call this function with parameter of type (b -> b) ...
|
// we call this function with parameter of type (b -> b) ...
|
||||||
// giving these substitutions:
|
// giving these substitutions:
|
||||||
// a := b
|
// a := b
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Type } from "../type.js";
|
import { Type } from "../primitives/types.js";
|
||||||
import { typedFnType } from "../structures/types.js"
|
import { typedFnType } from "../structures/types.js"
|
||||||
import { Double } from "../primitives/types.js";
|
import { Double } from "../primitives/types.js";
|
||||||
|
|
||||||
|
|
|
||||||
85
main.js
85
main.js
|
|
@ -3,7 +3,8 @@ import { ModulePoint } from "./lib/point.js";
|
||||||
import { DefaultMap, pretty, prettyT } 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';
|
import { Type } from "./primitives/types.js";
|
||||||
|
import { assign, makeGeneric, unify } from './generics/generics.js';
|
||||||
|
|
||||||
class Context {
|
class Context {
|
||||||
constructor(mod) {
|
constructor(mod) {
|
||||||
|
|
@ -11,14 +12,14 @@ class Context {
|
||||||
this.types = new DefaultMap(() => new Set()); // instance to type
|
this.types = new DefaultMap(() => new Set()); // instance to type
|
||||||
this.instances = new DefaultMap(() => new Set()); // type to instance
|
this.instances = new DefaultMap(() => new Set()); // 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).add(t);
|
this.types.getdefault(i, true).add(t);
|
||||||
this.instances.getdefault(t, true).add(i);
|
this.instances.getdefault(t, true).add(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.functionsFrom = new DefaultMap(() => new Set()); // type to outgoing function
|
||||||
|
this.functionsTo = new DefaultMap(() => new Set()); // type to incoming function
|
||||||
|
|
||||||
for (const t of this.instances.m.keys()) {
|
for (const t of this.instances.m.keys()) {
|
||||||
if (t.symbol === symbolFunction) {
|
if (t.symbol === symbolFunction) {
|
||||||
// 't' is a function signature
|
// 't' is a function signature
|
||||||
|
|
@ -28,6 +29,24 @@ class Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this.typeVarAssigns = new Map();
|
||||||
|
|
||||||
|
// for (const t of this.instances.m.keys()) {
|
||||||
|
// if (t.typeVars) {
|
||||||
|
// for (const t2 of this.instances.m.keys()) {
|
||||||
|
// const genericT2 = (t2.typeVars === undefined)
|
||||||
|
// ? makeGeneric(() => t2)
|
||||||
|
// : t2;
|
||||||
|
// try {
|
||||||
|
// const unification = unify(t, t2);
|
||||||
|
// console.log(unification);
|
||||||
|
// } catch (e) {
|
||||||
|
// // skip
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
addToCtx({i, t}) {
|
addToCtx({i, t}) {
|
||||||
|
|
@ -38,13 +57,13 @@ class Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let ctx = new Context({l:[
|
let ctx = new Context({l:[
|
||||||
...ModuleStd.l,
|
...ModuleStd.l,
|
||||||
...ModulePoint.l,
|
...ModulePoint.l,
|
||||||
]});
|
]});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const prettyIT = ({i, t}) => ({
|
const prettyIT = ({i, t}) => ({
|
||||||
strI: isType(i) ? prettyT(i) : pretty(i),
|
strI: isType(i) ? prettyT(i) : pretty(i),
|
||||||
strT: prettyT(t),
|
strT: prettyT(t),
|
||||||
|
|
@ -74,7 +93,11 @@ async function topPrompt() {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
if (action === "list all types") {
|
if (action === "list all types") {
|
||||||
await listAllTypes();
|
await listInstances(Type);
|
||||||
|
// await listAllTypes();
|
||||||
|
}
|
||||||
|
if (action === "list all functions") {
|
||||||
|
await listAllFunctions();
|
||||||
}
|
}
|
||||||
if (action === "list all") {
|
if (action === "list all") {
|
||||||
await listAllInstances();
|
await listAllInstances();
|
||||||
|
|
@ -82,22 +105,46 @@ async function topPrompt() {
|
||||||
return topPrompt();
|
return topPrompt();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function listAllTypes() {
|
// 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 listAllFunctions() {
|
||||||
const choice = await select({
|
const choice = await select({
|
||||||
message: "select type:",
|
message: "select function:",
|
||||||
choices: [
|
choices: [
|
||||||
"(go back)",
|
"(go back)",
|
||||||
...[...ctx.instances.m.keys()].map(t => ({
|
...[...ctx.types.m.entries()]
|
||||||
value: t,
|
.filter(([i, types]) => {
|
||||||
name: prettyT(t),
|
for (const type of types) {
|
||||||
})),
|
if (type.symbol === symbolFunction)
|
||||||
]
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
})
|
||||||
|
.flatMap(toChoices),
|
||||||
|
],
|
||||||
});
|
});
|
||||||
if (choice === "(go back)") {
|
if (choice === "(go back)") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
await typeOptions(choice);
|
const {i, t} = choice;
|
||||||
return listAllTypes();
|
await functionOptions(i, t);
|
||||||
|
return listAllFunctions();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function typeOptions(t) {
|
async function typeOptions(t) {
|
||||||
|
|
@ -106,8 +153,9 @@ async function typeOptions(t) {
|
||||||
choices: [
|
choices: [
|
||||||
"(go back)",
|
"(go back)",
|
||||||
"list instances",
|
"list instances",
|
||||||
"list outgoing functions",
|
// "list outgoing functions",
|
||||||
"list incoming functions",
|
// "list incoming functions",
|
||||||
|
"print raw",
|
||||||
"treat as instance",
|
"treat as instance",
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
@ -117,6 +165,9 @@ async function typeOptions(t) {
|
||||||
else if (choice === "list instances") {
|
else if (choice === "list instances") {
|
||||||
await listInstances(t);
|
await listInstances(t);
|
||||||
}
|
}
|
||||||
|
else if (choice === "print raw") {
|
||||||
|
console.log(pretty(t));
|
||||||
|
}
|
||||||
else if (choice === "treat as instance") {
|
else if (choice === "treat as instance") {
|
||||||
await instanceOptions(t, Type)
|
await instanceOptions(t, Type)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { fnType, typedFnType } from "../structures/types.js";
|
import { fnType, typedFnType } from "../structures/types.js";
|
||||||
import { Type } from "../type.js";
|
import { Type } from "./types.js";
|
||||||
import { Bool } from "./types.js";
|
import { Bool } from "./types.js";
|
||||||
|
|
||||||
const eqBool = x => y => x === y;
|
const eqBool = x => y => x === y;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { typedFnType } from "../structures/types.js";
|
import { typedFnType } from "../structures/types.js";
|
||||||
import { Type } from "../type.js";
|
import { Type } from "./types.js";
|
||||||
import {Byte, Bool} from "./types.js";
|
import {Byte, Bool} from "./types.js";
|
||||||
|
|
||||||
const eqByte = x => y => x === y;
|
const eqByte = x => y => x === y;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { typedFnType } from "../structures/types.js";
|
import { typedFnType } from "../structures/types.js";
|
||||||
import { Type } from "../type.js";
|
import { Type } from "./types.js";
|
||||||
import {Char, Bool} from "./types.js";
|
import {Char, Bool} from "./types.js";
|
||||||
|
|
||||||
const eq = x => y => x === y;
|
const eq = x => y => x === y;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { typedFnType } from "../structures/types.js";
|
import { typedFnType } from "../structures/types.js";
|
||||||
import { Type } from "../type.js";
|
import { Type } from "./types.js";
|
||||||
import {Bool, Double} from "./types.js";
|
import {Bool, Double} from "./types.js";
|
||||||
|
|
||||||
export const addDouble = x => y => x + y;
|
export const addDouble = x => y => x + y;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { typedFnType } from "../structures/types.js";
|
import { typedFnType } from "../structures/types.js";
|
||||||
import { Type } from "../type.js";
|
import { Type } from "./types.js";
|
||||||
|
|
||||||
import {Bool, Int} from "./types.js";
|
import {Bool, Int} from "./types.js";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,3 +5,5 @@ 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: [] };
|
export const Char = { symbol: Symbol('Char') , params: [] };
|
||||||
|
|
||||||
|
export const Type = { symbol: Symbol('Type'), params: [] };
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Type } from "../type.js";
|
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";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { fnType, typedFnType } from "./types.js";
|
import { fnType, typedFnType } from "./types.js";
|
||||||
import { Type } from "../type.js";
|
import { Type } from "../primitives/types.js";
|
||||||
import { Int } from "../primitives/types.js";
|
import { Int } from "../primitives/types.js";
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
import { makeGeneric } from "../generics/generics.js";
|
||||||
import { lsType } from "./types.js";
|
import { lsType } from "./types.js";
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
import { makeGeneric } from "../generics/generics.js";
|
||||||
import { Type } from "../type.js";
|
import { Type } from "../primitives/types.js";
|
||||||
import { typedFnType } from "./types.js";
|
import { typedFnType } from "./types.js";
|
||||||
import { prodType } from "./types.js";
|
import { prodType } from "./types.js";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
import { prodType } from "./types.js";
|
import { prodType } from "./types.js";
|
||||||
import { Type } from "../type.js";
|
import { Type } from "../primitives/types.js";
|
||||||
import { typedFnType } from "./types.js";
|
import { typedFnType } from "./types.js";
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
import { makeGeneric } from "../generics/generics.js";
|
||||||
import { sumType } from "./types.js";
|
import { sumType } from "./types.js";
|
||||||
|
|
||||||
const constructorLeft = left => ({variant: "L", value: left });
|
export const constructorLeft = left => ({variant: "L", value: left });
|
||||||
const constructorRight = right => ({variant: "R", value: right});
|
export const constructorRight = right => ({variant: "R", value: right});
|
||||||
|
|
||||||
// signature:
|
// signature:
|
||||||
// sum-type -> (leftType -> resultType, rightType -> resultType) -> resultType
|
// sum-type -> (leftType -> resultType, rightType -> resultType) -> resultType
|
||||||
const match = sum => handlers => sum.variant === "L"
|
export const match = sum => handlers => sum.variant === "L"
|
||||||
? handlers.left(sum.value)
|
? handlers.left(sum.value)
|
||||||
: handlers.right(sum.value);
|
: handlers.right(sum.value);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +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 { Type } from "../primitives/types.js";
|
||||||
import { DefaultMap } from "../util.js";
|
import { DefaultMap } from "../util.js";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
6
type.js
6
type.js
|
|
@ -1,11 +1,7 @@
|
||||||
import { Bool } from "./primitives/types.js";
|
import { Bool, Type } from "./primitives/types.js";
|
||||||
import { typedFnType } from "./structures/types.js";
|
import { typedFnType } from "./structures/types.js";
|
||||||
import { deepEqual } from "./util.js";
|
import { deepEqual } from "./util.js";
|
||||||
|
|
||||||
// TODO: 'Type' (and its instances) are itself instances of (String,[Type]) (=the product type of String and list of Type)
|
|
||||||
// so is 'Type' just an alias for (String, [Type])
|
|
||||||
export const Type = { symbol: Symbol('Type'), params: [] };
|
|
||||||
|
|
||||||
export const getSymbol = type => type.symbol;
|
export const getSymbol = type => type.symbol;
|
||||||
export const getParams = type => type.params;
|
export const getParams = type => type.params;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { makeGeneric } from "../generics/generics";
|
import { makeGeneric } from "../generics/generics";
|
||||||
import { Type } from "../type";
|
import { Type } from "../primitives/types";
|
||||||
import { typedFnType } from "../structures/types";
|
import { typedFnType } from "../structures/types";
|
||||||
import { Bool, Byte, Char, Double, Int } from "../primitives/types";
|
import { Bool, Byte, Char, Double, Int } from "../primitives/types";
|
||||||
import { deepEqual } from "../util";
|
import { deepEqual } from "../util";
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
import { makeGeneric } from "../generics/generics.js";
|
||||||
import { addDouble, mulDouble } from "../primitives/double.js";
|
import { addDouble, mulDouble } from "../primitives/double.js";
|
||||||
import { addInt, mulInt } from "../primitives/int.js";
|
import { addInt, mulInt } from "../primitives/int.js";
|
||||||
import { Type } from "../type.js";
|
import { Type } from "../primitives/types.js";
|
||||||
import { typedFnType, typedFnType2 } from "../structures/types.js";
|
import { typedFnType, typedFnType2 } from "../structures/types.js";
|
||||||
import { Double, Int } from "../primitives/types.js";
|
import { Double, Int } from "../primitives/types.js";
|
||||||
import { numDictType } from "./num_type.js";
|
import { numDictType } from "./num_type.js";
|
||||||
|
|
@ -10,14 +10,15 @@ export const getAdd = numDict => numDict.add;
|
||||||
export const getMul = numDict => numDict.mul;
|
export const getMul = numDict => numDict.mul;
|
||||||
|
|
||||||
// getAdd and getMul have same (generic) type:
|
// getAdd and getMul have same (generic) type:
|
||||||
|
// NumDict a -> a -> a -> a
|
||||||
const [getAddMulFnType, typesOfFns] = typedFnType2(fnType =>
|
const [getAddMulFnType, typesOfFns] = typedFnType2(fnType =>
|
||||||
makeGeneric(a => fnType({
|
makeGeneric(a =>
|
||||||
in: numDictType(a),
|
fnType
|
||||||
out: fnType({
|
(numDictType(a))
|
||||||
in: a,
|
(fnType
|
||||||
out: fnType({in: a, out: a}),
|
(a)
|
||||||
}),
|
(fnType(a)(a))
|
||||||
})));
|
)));
|
||||||
|
|
||||||
export const ModuleNum = {l:[
|
export const ModuleNum = {l:[
|
||||||
...typedFnType(numDictType, fnType => fnType({in: Type, out: Type})),
|
...typedFnType(numDictType, fnType => fnType({in: Type, out: Type})),
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,28 @@
|
||||||
import { assign } from "../generics/generics.js";
|
import { assign } from "../generics/generics.js";
|
||||||
import { makeGeneric } from "../generics/generics.js";
|
import { makeGeneric } from "../generics/generics.js";
|
||||||
import { fnType } from "../structures/function.js";
|
|
||||||
import { Double, Int } from "../primitives/types.js";
|
import { Double, Int } from "../primitives/types.js";
|
||||||
|
import { fnType } from "../structures/types.js";
|
||||||
|
import { pretty } from "../util.js";
|
||||||
import { getMul, NumInstances } from "./num.js";
|
import { getMul, NumInstances } from "./num.js";
|
||||||
import { numDictType } from "./num_type.js";
|
import { numDictType } from "./num_type.js";
|
||||||
|
|
||||||
const square = numDict => x => getMul(numDict)(x)(x);
|
const square = numDict => x => getMul(numDict)(x)(x);
|
||||||
|
|
||||||
|
// NumDict a -> a -> a
|
||||||
const squareFnType = makeGeneric(a =>
|
const squareFnType = makeGeneric(a =>
|
||||||
fnType({
|
fnType
|
||||||
in: numDictType(a),
|
(numDictType(a))
|
||||||
out: fnType({
|
(fnType(a)(a))
|
||||||
in: a,
|
);
|
||||||
out: a,
|
|
||||||
}),
|
|
||||||
}));
|
|
||||||
|
|
||||||
console.log("should be: Int -> Int");
|
console.log("should be: Int -> Int");
|
||||||
console.log(assign(squareFnType, makeGeneric(() => numDictType(Int))));
|
console.log(pretty(assign(squareFnType, makeGeneric(() => numDictType(Int)))));
|
||||||
|
|
||||||
console.log("should be: Double -> Double");
|
console.log("should be: Double -> Double");
|
||||||
console.log(assign(squareFnType, makeGeneric(() => numDictType(Double))));
|
console.log(pretty(assign(squareFnType, makeGeneric(() => numDictType(Double)))));
|
||||||
|
|
||||||
// to call 'square' we need:
|
// to call 'square' we need:
|
||||||
|
// - the type of our argument (=Int)
|
||||||
// - access to a mapping from types to their typeclass instantiation
|
// - access to a mapping from types to their typeclass instantiation
|
||||||
// - to know that our argument is 'Int'
|
|
||||||
console.log("");
|
console.log("");
|
||||||
console.log(square(NumInstances.get(Int))(42n)); // 1764n
|
console.log(square(NumInstances.get(Int))(42n)); // 1764n
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
// const resolveTypeClass = (mapping, )
|
|
||||||
2
typed.js
2
typed.js
|
|
@ -1,5 +1,5 @@
|
||||||
import { typedFnType } from "./structures/types.js";
|
import { typedFnType } from "./structures/types.js";
|
||||||
import { Type } from "./type.js";
|
import { Type } from "./primitives/types.js";
|
||||||
|
|
||||||
// Everything is (implicitly) typed by the Any type.
|
// Everything is (implicitly) typed by the Any type.
|
||||||
export const Any = { symbol: Symbol('Any'), params: [] };
|
export const Any = { symbol: Symbol('Any'), params: [] };
|
||||||
|
|
|
||||||
11
util.js
11
util.js
|
|
@ -1,3 +1,6 @@
|
||||||
|
import { inspect } from 'node:util';
|
||||||
|
import { symbolFunction, symbolList, symbolProduct, symbolSum } from './structures/types.js';
|
||||||
|
|
||||||
// re-inventing the wheel:
|
// re-inventing the wheel:
|
||||||
export function deepEqual(a, b) {
|
export function deepEqual(a, b) {
|
||||||
if (a === b) return true; // <- shallow equality and primitives
|
if (a === b) return true; // <- shallow equality and primitives
|
||||||
|
|
@ -45,18 +48,18 @@ export class DefaultMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pretty print type
|
||||||
export function prettyT(type) {
|
export function prettyT(type) {
|
||||||
if (type.typeVars) {
|
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)}`;
|
||||||
}
|
}
|
||||||
|
return prettyT(type.type);
|
||||||
|
}
|
||||||
if (type.symbol === symbolFunction) {
|
if (type.symbol === symbolFunction) {
|
||||||
return `${prettyT(type.params[0])} -> ${prettyT(type.params[1])}`;
|
return `${prettyT(type.params[0])} -> ${prettyT(type.params[1])}`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue