diff --git a/generic.js b/generic.js deleted file mode 100644 index 259065a..0000000 --- a/generic.js +++ /dev/null @@ -1,172 +0,0 @@ -import { Bool, Int } from "./primitives/symbols.js"; -import { fnType, lsType } from "./type_registry.js"; -import { deepEqual } from "./util.js"; - -// let nextSymbol = 'a'; - -// export const makeGeneric = callback => { -// const typeParam = Symbol(nextSymbol); -// nextSymbol = String.fromCharCode(nextSymbol.charCodeAt(0) + 1); -// return { -// typeParam, -// type: callback(typeParam), -// }; -// }; - - -import { inspect } from 'node:util'; - -function pretty(obj) { - return inspect(obj, {colors: true}); -} - -export const matchGeneric = ( - {typeVars: formalTypeVars, type: formalType}, - {typeVars: actualTypeVars, type: actualType}, -) => { - if (deepEqual(formalType, actualType)) { - return { - substitutions: new Map(), - typeVars: new Set([ - ...actualTypeVars, - ...formalTypeVars]), - type: actualType, - } - } - - if (formalTypeVars.has(formalType)) { - // simplest case: substitute formal type param - return { - substitutions: new Map([[formalType, actualType]]), - typeVars: new Set([ - ...actualTypeVars, - ...formalTypeVars].filter(a => a !== formalType)), - type: actualType, - }; - } - - if (formalType.in !== undefined) { - // function type - if (actualType.in === undefined) { - throw new Error(`cannot assign ${pretty(actualType)} to ${pretty(formalType)}`); - } - else { - // both are function type - const inType = matchGeneric({typeVars: formalTypeVars, type: formalType.in}, {typeVars: actualTypeVars, type: actualType.in}); - const outType = matchGeneric({typeVars: formalTypeVars, type: formalType.out}, {typeVars: actualTypeVars, type: actualType.out}); - // check for conflicts between 'in' and 'out' subsitutions - for (const [typeVar, actual] of inType.substitutions) { - if (outType.substitutions.has(typeVar)) { - if (!deepEqual(actual, outType.substitutions.get(typeVar))) { - throw new Error(`conflicting assignment for ${pretty(typeVar)}: ${pretty(a)}`); - } - } - } - // merge substitutions - const newSubstitutions = new Map([ - ...inType.substitutions, - ...outType.substitutions, - ]); - const newTypeVars = new Set([ - ...actualTypeVars, - ...formalTypeVars].filter(a => !newSubstitutions.has(a))); - return { - substitutions: newSubstitutions, - typeVars: newTypeVars, - type: fnType({in: inType.type, out: outType.type}), - }; - } - } - - if (formalType.listOf !== undefined) { - // list type - if (actualType.listOf === undefined) { - throw new Error(`cannot assign ${pretty(actualType)} to ${pretty(formalType)}`); - } - else { - // both are list type - const elementType = matchGeneric( - {typeVars: formalTypeVars, type: formalType.listOf}, - {typeVars: actualTypeVars, type: actualType.listOf}); - return { - substitutions: elementType.substitutions, - typeVars: new Set([ - ...actualTypeVars, - ...formalTypeVars].filter(a => !elementType.substitutions.has(a))), - type: lsType(elementType.type), - }; - } - } - throw new Error("i don't know what to do :(") -}; - -export const matchConcrete = ({typeVars, type: formalType}, actualType) => { - return matchGeneric({typeVars, type: formalType}, {typeVars: new Set(), type: actualType}); -}; - -export const substitute = (type, substitutions) => { - if (substitutions.has(type)) { - return substitutions.get(type); - } - if (type.listOf !== undefined) { - // list type - return lsType(substitute(type.listOf, substitutions)); - } - if (type.in !== undefined) { - // function type - return fnType({ - in: substitute(type.in, substitutions), - out: substitute(type.out, substitutions), - }) - } - throw new Error("i don't know what to do :("); -} - -export const assign = (genFnType, paramType) => { - const matchedInType = matchGeneric({ - typeVars: genFnType.typeVars, - type: genFnType.type.in, - }, paramType); - const substitutedOutType = substitute(genFnType.type.out, matchedInType.substitutions); - return { - typeVars: matchedInType.typeVars, - type: substitutedOutType, - }; -} - -const a = Symbol('a'); -const formal = { - typeVars: new Set([a]), - type: fnType({in: a, out: Int}), -}; -const actual = fnType({in: lsType(Bool), out: Int}); -console.log(matchConcrete(formal, actual)); - - -const b = Symbol('b'); -const c = Symbol('c'); -const formal2 = { - typeVars: new Set([a, b]), - type: fnType({in: fnType({in: a, out: a}), out: b}), -} -const actual2 = { - typeVars: new Set([c]), - type: fnType({in: fnType({in: Bool, out: Bool}), out: c}), -} -console.log(matchGeneric(formal2, actual2)); - - -const mapFnType = { - typeVars: new Set([a, b]), - type: fnType({ - in: fnType({in: a, out: b}), - out: fnType({in: lsType(a), out: lsType(b)}), - }), -}; -const idFnType = { - typeVars: new Set([c]), - type: fnType({in: c, out: c}), -}; - -// should be: listOf(c) -> listOf(c) -console.log(assign(mapFnType, idFnType)); diff --git a/interfaces/serializable.js b/interfaces/serializable.js index abac7f6..1c1d2f7 100644 --- a/interfaces/serializable.js +++ b/interfaces/serializable.js @@ -1,10 +1,10 @@ -import { fnType, lsType } from "../type_registry.js"; +import { fnType, getListType } from "../type_registry.js"; import {Type, Function} from "../metacircular.js"; import { Byte } from "../primitives/symbols.js"; export const Serializable = Symbol('Serializable'); -const ListOfByte = lsType(Byte); +const ListOfByte = getListType(Byte); const serializeFnType = fnType({in: Serializable, out: ListOfByte}); const deserializeFnType = fnType({in: ListOfByte, out: Serializable}); diff --git a/lib/values.js b/lib/values.js index ea9df41..ec46389 100644 --- a/lib/values.js +++ b/lib/values.js @@ -2,15 +2,15 @@ import {Int, Bool, Double, Byte} from "../primitives/symbols.js"; import { makeListModule } from "../structures/list_common.js"; import { makeProductType } from "../structures/product.js"; import { makeSumType } from "../structures/sum.js"; -import { lsType, prodType, sumType } from "../type_registry.js"; +import { getListType, prodType, sumType } from "../type_registry.js"; -const ListOfDouble = lsType(Double); +const ListOfDouble = getListType(Double); const ListOfDoubleModule = makeListModule(Double); -const ListOfListOfDouble = lsType(ListOfDouble); +const ListOfListOfDouble = getListType(ListOfDouble); const ListOfListOfDoubleModule = makeListModule(ListOfDouble); -const ListOfByte = lsType(Byte); +const ListOfByte = getListType(Byte); const ListOfByteModule = makeListModule(Byte); export const ModuleValues = [ diff --git a/structures/list.js b/structures/list.js index f1d5e9c..b2f528a 100644 --- a/structures/list.js +++ b/structures/list.js @@ -1,4 +1,4 @@ -import { fnType, lsType } from "../type_registry.js"; +import { fnType, getListType } from "../type_registry.js"; import {Type, Function} from "../metacircular.js"; import { makeListModule } from "./list_common.js"; import { Module } from "./module.js"; @@ -7,7 +7,7 @@ const Type_to_Type = fnType({in: Type, out: Type}); const Type_to_Module = fnType({in: Type, out: Module}); export const ModuleList = [ - {i: lsType , t: Type_to_Type}, + {i: getListType , t: Type_to_Type}, {i: Type_to_Type , t: Function}, {i: makeListModule, t: Type_to_Module}, diff --git a/structures/list_common.js b/structures/list_common.js index f1dbc45..fe8762a 100644 --- a/structures/list_common.js +++ b/structures/list_common.js @@ -1,4 +1,4 @@ -import { fnType, lsType } from "../type_registry.js"; +import { fnType, getListType } from "../type_registry.js"; import {Type, Function} from "../metacircular.js"; import {Int, Byte} from "../primitives/symbols.js"; @@ -22,7 +22,7 @@ const byteListImpl = { export const makeListModule = elementType => { // List type depends on elementType // generating it another time, will give the same type (structurally equivalent): - const ListOfElement = lsType(elementType); + const ListOfElement = getListType(elementType); const getFnType1 = fnType({in: Int , out: elementType}); const getFnType = fnType({in: ListOfElement, out: getFnType1}); diff --git a/structures/module.js b/structures/module.js index 528206f..5f1e6ac 100644 --- a/structures/module.js +++ b/structures/module.js @@ -1,7 +1,7 @@ import { makeListModule } from "./list_common.js"; import { Typed } from "../typed.js"; -import { lsType } from "../type_registry.js"; +import { getListType } from "../type_registry.js"; -export const Module = lsType(Typed); // a Module is a list of Typeds +export const Module = getListType(Typed); // a Module is a list of Typeds export const ModuleModule = makeListModule(Typed); // the module containing operations on Module diff --git a/structures/product.js b/structures/product.js index 9dfec02..9814a06 100644 --- a/structures/product.js +++ b/structures/product.js @@ -1,5 +1,5 @@ import { fnType, prodType } from "../type_registry.js"; -import { Function, Type } from "../metacircular.js"; +import { Function } from "../metacircular.js"; // In JS, all products are encoded in the same way: const constructor = left => right => ({left, right}); @@ -8,17 +8,15 @@ const getRight = product => product.right; // Given two types A and B, create the type (A × B). export const makeProductType = (leftType, rightType) => { - const pType = prodType(leftType, rightType); + const productType = prodType(leftType, rightType); - const leftFnType = fnType({in: pType, out: leftType}); - const rightFnType = fnType({in: pType, out: rightType}); + const leftFnType = fnType({in: productType, out: leftType}); + const rightFnType = fnType({in: productType, out: rightType}); - const constructorBoundType = fnType({in: rightType, out: pType}); + const constructorBoundType = fnType({in: rightType, out: productType}); const constructorType = fnType({in: leftType , out: constructorBoundType}); return [ - {i: pType, t: Type}, - {i: getLeft , t: leftFnType}, {i: getRight , t: rightFnType}, {i: leftFnType , t: Function}, diff --git a/structures/sum.js b/structures/sum.js index 8d38982..85bbf27 100644 --- a/structures/sum.js +++ b/structures/sum.js @@ -5,8 +5,6 @@ import { Module } from "./module.js"; const constructorLeft = left => ({variant: "L", value: left }); const constructorRight = right => ({variant: "R", value: right}); -// signature: -// sum-type -> (leftType -> resultType, rightType -> resultType) -> resultType const match = sum => handlers => sum.variant === "L" ? handlers.left(sum.value) : handlers.right(sum.value); @@ -33,7 +31,6 @@ export const makeSumType = (leftType, rightType) => { }; return [ - {i: sType , t: Type}, {i: constructorLeft , t: constructorLeftType}, {i: constructorRight , t: constructorRightType}, {i: constructorLeftType , t: Function}, diff --git a/type_registry.js b/type_registry.js index 2d6b880..dbc0ec8 100644 --- a/type_registry.js +++ b/type_registry.js @@ -20,16 +20,14 @@ export const fnType = ({in: inType, out: outType}) => { // Global store of list types: const listTypes = new Map(); -export const lsType = (elementType) => { +export function getListType(elementType) { if (listTypes.has(elementType)) { // only generate each list type once // this would not be necessary if we could define our own equality and hash functions on objects in JavaScript. return listTypes.get(elementType); } else { - const type = { - listOf: elementType, - }; + const type = Symbol('ListOf:' + elementType.toString()); listTypes.set(elementType, type); return type; }