From 3d08485a086f42a748a529bcd7b605e7489d7ea9 Mon Sep 17 00:00:00 2001 From: Joeri Exelmans Date: Mon, 17 Mar 2025 20:14:57 +0100 Subject: [PATCH] list of byte encoded as JS Uint8Array + demo more readable --- lib/values.js | 24 ++++++++----- main.js | 16 ++++++--- primitives/byte.js | 15 +++++++++ primitives/symbols.js | 3 +- structures/list_common.js | 71 +++++++++++++++++++++++++++++---------- structures/product.js | 10 +++--- 6 files changed, 102 insertions(+), 37 deletions(-) create mode 100644 primitives/byte.js diff --git a/lib/values.js b/lib/values.js index d548dff..ec46389 100644 --- a/lib/values.js +++ b/lib/values.js @@ -1,14 +1,17 @@ -import {Int, Bool, Double} from "../primitives/symbols.js"; +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 { getListType, prodType, sumType } from "../type_registry.js"; -const ListOfInt = getListType(Int); -const ListOfIntModule = makeListModule(Int); +const ListOfDouble = getListType(Double); +const ListOfDoubleModule = makeListModule(Double); -const ListOfListOfInt = getListType(ListOfInt); -const ListOfListOfIntModule = makeListModule(ListOfInt); +const ListOfListOfDouble = getListType(ListOfDouble); +const ListOfListOfDoubleModule = makeListModule(ListOfDouble); + +const ListOfByte = getListType(Byte); +const ListOfByteModule = makeListModule(Byte); export const ModuleValues = [ {i: 0n, t: Int}, @@ -16,12 +19,15 @@ export const ModuleValues = [ {i: false, t: Bool}, {i: 3.14159265359, t: Double}, - {i: {l:[42n, 43n]} , t: ListOfInt}, - {i: {l:[{l:[42n, 43n]}, {l:[999n]}]}, t: ListOfListOfInt}, + {i: {l:[4.2, 7.6]} , t: ListOfDouble}, + {i: {l:[{l:[4.2, 7.6]}, {l:[4.3]}]}, t: ListOfListOfDouble}, + + {i: new Uint8Array([1,2,3]), t: ListOfByte}, // i'm lazy - ...ListOfIntModule, - ...ListOfListOfIntModule, + ...ListOfDoubleModule, + ...ListOfListOfDoubleModule, + ...ListOfByteModule, ...makeProductType(Int, Bool), diff --git a/main.js b/main.js index e31dcc4..e4553da 100644 --- a/main.js +++ b/main.js @@ -29,14 +29,20 @@ class Context { 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)) { - 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); + 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})`); } } } diff --git a/primitives/byte.js b/primitives/byte.js new file mode 100644 index 0000000..987ffd7 --- /dev/null +++ b/primitives/byte.js @@ -0,0 +1,15 @@ +import { fnType } from "../type_registry.js"; +import {Type, Function} from "../metacircular.js"; +import {Byte, Bool} from "./symbols.js"; + +const eqByte = x => y => x === y; + +const Byte_to_Bool = fnType({in: Byte, out: Bool}); +const Byte_to_Byte_to_Bool = fnType({in: Byte, out: Byte_to_Bool}); + +export const ModuleByte = [ + {i: Byte , t: Type }, + {i: Byte_to_Bool , t: Function }, + {i: Byte_to_Byte_to_Bool , t: Function }, + {i: eqByte , t: Byte_to_Byte_to_Bool }, +]; diff --git a/primitives/symbols.js b/primitives/symbols.js index d4c7591..99e0e2f 100644 --- a/primitives/symbols.js +++ b/primitives/symbols.js @@ -2,4 +2,5 @@ export const Bool = Symbol('Bool'); export const Int = Symbol('Int'); -export const Double = Symbol('Double'); \ No newline at end of file +export const Double = Symbol('Double'); +export const Byte = Symbol('Byte'); \ No newline at end of file diff --git a/structures/list_common.js b/structures/list_common.js index 3a90ebe..fe8762a 100644 --- a/structures/list_common.js +++ b/structures/list_common.js @@ -1,32 +1,69 @@ import { fnType, getListType } from "../type_registry.js"; import {Type, Function} from "../metacircular.js"; -import {Int} from "../primitives/symbols.js"; +import {Int, Byte} from "../primitives/symbols.js"; + +// 'normal' implementation +const emptyList = {l:[]}; +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])}); + +const byteListImpl = { + // specialization + emptyList: new Uint8Array(), + get: ls => i => ls[i], + put: ls => i => elem => { + res = new Uint8Array(ls); // creates copy + res[i] = elem; + return res; + } +} export const makeListModule = elementType => { // List type depends on elementType // generating it another time, will give the same type (structurally equivalent): const ListOfElement = getListType(elementType); - const emptyList = {l:[]}; + const getFnType1 = fnType({in: Int , out: elementType}); + const getFnType = fnType({in: ListOfElement, out: getFnType1}); - const get = ls => i => ls.l[i]; - const getBoundType = fnType({in: Int, out: elementType}); - const getType = fnType({in: ListOfElement, out: getBoundType}); + const putFnType2 = fnType({in: elementType , out: ListOfElement}); + const putFnType1 = fnType({in: Int , out: putFnType2}); + const putFnType = fnType({in: ListOfElement, out: putFnType1}); - const push = ls => elem => ({l:ls.l.concat([elem])}); - const pushBoundType = fnType({in: elementType, out: ListOfElement}); - const pushType = fnType({in: ListOfElement, out: pushBoundType}); + // const pushFnType1 = fnType({in: elementType , out: ListOfElement}); + // const pushFnType = fnType({in: ListOfElement, out: pushFnType1}); - return [ + const common = [ {i: ListOfElement, t: Type}, - {i: emptyList , t: ListOfElement}, - - {i: get , t: getType}, - {i: getType , t: Function}, - {i: getBoundType , t: Function}, - {i: push , t: pushType}, - {i: pushType , t: Function}, - {i: pushBoundType, t: Function}, + {i: getFnType , t: Function}, + {i: getFnType1 , t: Function}, + + {i: putFnType , t: Function}, + {i: putFnType1, t: Function}, + {i: putFnType2, t: Function}, + + // {i: pushFnType , t: Function}, + // {i: pushFnType1, t: Function}, ]; + + if (elementType === Byte) { + // specialization: use Uint8Array instead of JS array + return [ + ...common, + {i: byteListImpl.emptyList , t: ListOfElement}, + {i: byteListImpl.get , t: getFnType}, + {i: byteListImpl.put , t: putFnType}, + ]; + } + else { + return [ + ...common, + {i: emptyList , t: ListOfElement}, + {i: get , t: getFnType}, + {i: put , t: putFnType}, + // {i: push , t: pushFnType}, + ]; + } }; diff --git a/structures/product.js b/structures/product.js index 3d485fc..9814a06 100644 --- a/structures/product.js +++ b/structures/product.js @@ -3,8 +3,8 @@ import { Function } from "../metacircular.js"; // In JS, all products are encoded in the same way: const constructor = left => right => ({left, right}); -const left = product => product.left; -const right = product => product.right; +const getLeft = product => product.left; +const getRight = product => product.right; // Given two types A and B, create the type (A × B). export const makeProductType = (leftType, rightType) => { @@ -14,11 +14,11 @@ export const makeProductType = (leftType, rightType) => { const rightFnType = fnType({in: productType, out: rightType}); const constructorBoundType = fnType({in: rightType, out: productType}); - const constructorType = fnType({in: leftType, out: constructorBoundType}); + const constructorType = fnType({in: leftType , out: constructorBoundType}); return [ - {i: left , t: leftFnType}, - {i: right , t: rightFnType}, + {i: getLeft , t: leftFnType}, + {i: getRight , t: rightFnType}, {i: leftFnType , t: Function}, {i: rightFnType, t: Function},