diff --git a/generic.js b/generic.js index 259065a..46c136c 100644 --- a/generic.js +++ b/generic.js @@ -2,16 +2,35 @@ 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 => { + // type variables to make available: + const a = Symbol('a'); + const b = Symbol('b'); + const c = Symbol('c'); + const d = Symbol('d'); + const e = Symbol('e'); + const type = callback(a,b,c,d,e); + return { + typeVars: occurring(type, new Set([a,b,c,d,e])), + type, + }; +}; -// export const makeGeneric = callback => { -// const typeParam = Symbol(nextSymbol); -// nextSymbol = String.fromCharCode(nextSymbol.charCodeAt(0) + 1); -// return { -// typeParam, -// type: callback(typeParam), -// }; -// }; +const occurring = (type, typeVars) => { + if (typeVars.has(type)) { + return new Set([type]); + } + if (type.in !== undefined) { + // function type + return new Set([ + ...occurring(type.in, typeVars), + ...occurring(type.out, typeVars)]); + } + if (type.listOf !== undefined) { + return occurring(type.listOf, typeVars); + } + return new Set(); +} import { inspect } from 'node:util'; @@ -119,7 +138,7 @@ export const substitute = (type, substitutions) => { out: substitute(type.out, substitutions), }) } - throw new Error("i don't know what to do :("); + return type; } export const assign = (genFnType, paramType) => { @@ -134,39 +153,26 @@ export const assign = (genFnType, paramType) => { }; } -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)); +// a -> Int +const a_to_Int = makeGeneric(a => fnType({in: a, out: Int})); +const Bool_to_Int = fnType({in: lsType(Bool), out: Int}); +console.log(matchConcrete(a_to_Int, Bool_to_Int)); +// (a -> a) -> b +const fnType2 = makeGeneric((a,b) => fnType({in: fnType({in: a, out: a}), out: b})); +// (Bool -> Bool) -> a +const fnType3 = makeGeneric(a => fnType({in: fnType({in: Bool, out: Bool}), out: a})); +console.log(matchGeneric(fnType2, fnType3)); -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({ +// (a -> b) -> [a] -> [b] +const mapFnType = makeGeneric((a,b) => + 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}), -}; + })); +// a -> a +const idFnType = makeGeneric(a => + fnType({in: a, out: a})); -// should be: listOf(c) -> listOf(c) +// should be: [a] -> [a] console.log(assign(mapFnType, idFnType));