branching and very basic merging of slots
This commit is contained in:
parent
614e6c0fdb
commit
3978f7f835
32 changed files with 684 additions and 420 deletions
42
scripts/generics.js
Normal file
42
scripts/generics.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { Bool, Int } from "../primitives/types.js";
|
||||
import { fnType, lsType, prettyT } from "../structures/types.js";
|
||||
import { assign, makeGeneric, unify } from "../generics/generics.js";
|
||||
|
||||
// a -> Int
|
||||
const a_to_Int = makeGeneric(a => fnType(a)(Int));
|
||||
// Bool -> Int
|
||||
const Bool_to_Int = makeGeneric(() => fnType(lsType(Bool))(Int));
|
||||
console.log("should be: [Bool] -> Int")
|
||||
console.log(prettyT(unify(a_to_Int, Bool_to_Int)));
|
||||
|
||||
// (a -> a) -> b
|
||||
const fnType2 = makeGeneric((a,b) => fnType(fnType(a)(a))(b));
|
||||
// (Bool -> Bool) -> a
|
||||
const fnType3 = makeGeneric(a => fnType(fnType(Bool)(Bool))(a));
|
||||
console.log("should be: (Bool -> Bool) -> a");
|
||||
console.log(prettyT(unify(fnType2, fnType3)));
|
||||
|
||||
// (a -> b) -> [a] -> [b]
|
||||
const mapFnType = makeGeneric((a,b) =>
|
||||
fnType
|
||||
(fnType(a)(b))
|
||||
(fnType(lsType(a))(lsType(b))))
|
||||
// a -> a
|
||||
const idFnType = makeGeneric((_,__,c) =>
|
||||
fnType(c)(c));
|
||||
console.log("should be: [c] -> [c]");
|
||||
console.log(prettyT(assign(mapFnType, idFnType)));
|
||||
|
||||
// (a -> Int) -> [a] -> a
|
||||
const weirdFnType = makeGeneric(a =>
|
||||
fnType
|
||||
(fnType(a)(Int))
|
||||
(fnType
|
||||
(lsType(a))
|
||||
(a)))
|
||||
// we call this function with parameter of type (b -> b) ...
|
||||
// giving these substitutions:
|
||||
// a := b
|
||||
// b := Int
|
||||
console.log("should be: [Int] -> Int");
|
||||
console.log(prettyT(assign(weirdFnType, idFnType)));
|
||||
|
|
@ -6,8 +6,8 @@ import { isFunction, prettyT } from '../structures/types.js';
|
|||
import { ModuleStd } from '../stdlib.js';
|
||||
import { Double, GenericType, Int, SymbolT, Type } from "../primitives/types.js";
|
||||
import { eqType } from '../primitives/type.js';
|
||||
import { Any } from '../typed.js';
|
||||
import { assign, assignFn, makeGeneric, onlyOccurring } from '../generics/generics.js';
|
||||
import { Any } from "../primitives/types.js";
|
||||
import { assignFn, makeGeneric, onlyOccurring } from '../generics/generics.js';
|
||||
|
||||
|
||||
// import {emitKeypressEvents} from 'node:readline';
|
||||
|
|
@ -48,6 +48,15 @@ class Context {
|
|||
|
||||
this.types.getdefault(i, true).add(t);
|
||||
this.types.getdefault(i, true).add(Any);
|
||||
if (t.typeVars) {
|
||||
// console.log("generic:", prettyT(t));
|
||||
this.types.getdefault(t, true).add(GenericType);
|
||||
}
|
||||
else {
|
||||
// console.log("non-generic:", prettyT(t));
|
||||
this.types.getdefault(t, true).add(Type);
|
||||
}
|
||||
|
||||
this.instances.getdefault(t, true).add(i);
|
||||
this.instances.getdefault(Any, true).add(i);
|
||||
}
|
||||
|
|
@ -318,7 +327,7 @@ async function callFunction(fn, fnT) {
|
|||
}
|
||||
else {
|
||||
inType = fnT.params[0];
|
||||
choices = [...ctx.instances.getdefault(inType)].flatMap(i => toChoices([i, ctx.types.getdefault(i)]));
|
||||
choices = [...ctx.instances.getdefault(inType)].flatMap(i => toChoices([i, [inType]]));
|
||||
}
|
||||
|
||||
const choice = await select({
|
||||
|
|
@ -337,7 +346,7 @@ async function callFunction(fn, fnT) {
|
|||
i = await createInstance(inType);
|
||||
t = inType;
|
||||
if (i === undefined) {
|
||||
return;
|
||||
return callFunction(fn, fnT);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -345,7 +354,8 @@ async function callFunction(fn, fnT) {
|
|||
t = choice.t;
|
||||
}
|
||||
const genT = t.typeVars ? t : makeGeneric(() => t);
|
||||
const assignedFnType = assignFn(fnT, genT);
|
||||
const genFnT = fnT.typeVars ? fnT : makeGeneric(() => fnT);
|
||||
const assignedFnType = assignFn(genFnT, genT);
|
||||
await apply(i, fn, assignedFnType);
|
||||
return callFunction(fn, fnT);
|
||||
}
|
||||
|
|
@ -417,11 +427,10 @@ async function transform(i, t) {
|
|||
|
||||
async function apply(i, fn, fnT) {
|
||||
const result = fn(i);
|
||||
// console.log(fn, '(', i, ')', '=', result);
|
||||
let resultType;
|
||||
// console.log(fnT);
|
||||
if (fnT.typeVars) {
|
||||
resultType = onlyOccurring(fnT.type.params[1], fnT.typeVars);
|
||||
const res = onlyOccurring(fnT.type.params[1], fnT.typeVars);
|
||||
resultType = res.typeVars.size > 0 ? res : res.type;
|
||||
}
|
||||
else {
|
||||
resultType = fnT.params[1];
|
||||
|
|
|
|||
28
scripts/num.js
Normal file
28
scripts/num.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { assign } from "../generics/generics.js";
|
||||
import { makeGeneric } from "../generics/generics.js";
|
||||
import { Double, Int } from "../primitives/types.js";
|
||||
import { fnType } from "../structures/types.js";
|
||||
import { pretty } from '../util/pretty.js';
|
||||
import { getMul, NumInstances } from "../typeclasses/num.js";
|
||||
import { numDictType } from "./num_type.js";
|
||||
|
||||
const square = numDict => x => getMul(numDict)(x)(x);
|
||||
|
||||
// NumDict a -> a -> a
|
||||
const squareFnType = makeGeneric(a =>
|
||||
fnType
|
||||
(numDictType(a))
|
||||
(fnType(a)(a))
|
||||
);
|
||||
|
||||
console.log("should be: Int -> Int");
|
||||
console.log(pretty(assign(squareFnType, makeGeneric(() => numDictType(Int)))));
|
||||
|
||||
console.log("should be: Double -> Double");
|
||||
console.log(pretty(assign(squareFnType, makeGeneric(() => numDictType(Double)))));
|
||||
|
||||
// to call 'square' we need:
|
||||
// - the type of our argument (=Int)
|
||||
// - access to a mapping from types to their typeclass instantiation
|
||||
console.log("");
|
||||
console.log(square(NumInstances.get(Int))(42n)); // 1764n
|
||||
|
|
@ -54,7 +54,7 @@ function benchmark(N) {
|
|||
const endTime = Date.now();
|
||||
return endTime - startTime;
|
||||
}
|
||||
const durCopying = (N <= 20000) ? copying() : "";
|
||||
const durCopying = (N <= 50000) ? copying() : "";
|
||||
console.log("copying:", durCopying, "ms");
|
||||
|
||||
// slower than slowest
|
||||
|
|
|
|||
122
scripts/versioning.js
Normal file
122
scripts/versioning.js
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
import { pretty } from "../util/pretty.js";
|
||||
import { newLiteral, transform, read, getReadDependencies, verifyValue } from "../versioning/value.js";
|
||||
import { merge, merge2, newSlot, overwrite } from "../versioning/slot.js";
|
||||
import createRBTree from "functional-red-black-tree";
|
||||
import { add, emptySet, RBTreeWrapper } from "../structures/set.js";
|
||||
|
||||
const inc = x => x + 1;
|
||||
|
||||
|
||||
console.log("##############");
|
||||
console.log("## Counting ##");
|
||||
console.log("##############");
|
||||
|
||||
const counterOneSlot = newSlot(Symbol('counter'))(newLiteral(1));
|
||||
const valueOne = read(counterOneSlot);
|
||||
console.log(pretty({valueOne}));
|
||||
const onePlusOne = transform(valueOne)(newLiteral(inc));
|
||||
console.log(pretty({onePlusOne}));
|
||||
const onePlusOnePlusOne = transform(onePlusOne)(newLiteral(inc));
|
||||
console.log(pretty({onePlusOnePlusOne}));
|
||||
|
||||
verifyValue(valueOne);
|
||||
verifyValue(onePlusOne);
|
||||
verifyValue(onePlusOnePlusOne);
|
||||
|
||||
|
||||
|
||||
console.log("#############");
|
||||
console.log("## Summing ##");
|
||||
console.log("#############");
|
||||
|
||||
const priceSlot = newSlot(Symbol('price'))(newLiteral(20.66));
|
||||
const taxSlot = newSlot(Symbol('tax'))(newLiteral(4.34));
|
||||
|
||||
const total =
|
||||
transform(read(priceSlot))(
|
||||
transform(read(taxSlot))(
|
||||
newLiteral(tax => price => price + tax)));
|
||||
|
||||
console.log(pretty({total}))
|
||||
|
||||
const totalPlusOne = transform(total)(newLiteral(inc));
|
||||
|
||||
console.log(pretty({totalPlusOne}));
|
||||
|
||||
verifyValue(totalPlusOne);
|
||||
|
||||
console.log("getReadDependencies(totalPlusOne):", getReadDependencies(totalPlusOne));
|
||||
|
||||
|
||||
|
||||
console.log("###############");
|
||||
console.log("## Branching ##");
|
||||
console.log("###############");
|
||||
|
||||
const fiveSlot = newSlot(Symbol('counter'))(newLiteral(5));
|
||||
const sixSlot = overwrite(fiveSlot)(newLiteral(6));
|
||||
const sevenSlot = overwrite(fiveSlot)(newLiteral(7));
|
||||
const eightSlot = overwrite(fiveSlot)(newLiteral(8));
|
||||
|
||||
const numCompare = x => y => {
|
||||
if (typeof(x) !== 'number' || typeof(y) !== 'number') {
|
||||
throw new Error(`was only meant to compare numbers! got ${x} and ${y}`);
|
||||
}
|
||||
return (x < y) ? -1 : (x > y) ? 1 : 0;
|
||||
};
|
||||
const intMerge = merge(numCompare);
|
||||
const intMerge2 = merge2(numCompare);
|
||||
|
||||
const sixSevenSlot = intMerge(sixSlot)(sevenSlot);
|
||||
const sevenEightSlot = intMerge(sevenSlot)(eightSlot);
|
||||
|
||||
// console.log(compareSlots(intCompare)(fiveSlot)(fiveSlot));
|
||||
// console.log(compareSlots(intCompare)(sixSlot)(sixSlot));
|
||||
// console.log(compareSlots(intCompare)(fiveSlot)(sixSlot));
|
||||
// console.log(compareSlots(intCompare)(sixSlot)(fiveSlot));
|
||||
|
||||
const sixSevenEightSlot = intMerge2(sixSevenSlot)(sevenEightSlot);
|
||||
|
||||
console.log(pretty({sixSevenEightSlot}));
|
||||
|
||||
// console.log("########################");
|
||||
// console.log("## Heterogeneous data ##");
|
||||
// console.log("########################");
|
||||
|
||||
// const strCompare = x => y => {
|
||||
// if (typeof(x) !== 'string' || typeof(y) !== 'string') {
|
||||
// throw new Error(`was only meant to compare strings! got ${x} and ${y}`);
|
||||
// }
|
||||
// return (x < y) ? -1 : (x > y) ? 1 : 0;
|
||||
// };
|
||||
|
||||
// // Slot<Int>
|
||||
// const numberOfSheepSlot = newSlot(Symbol('numberOfSheep'))(newLiteral(5));
|
||||
// const alternativeNumberOfSheepSlot = newSlot(Symbol('alternativeNumberOfSheep'))(newLiteral(6));
|
||||
// // Slot<String>
|
||||
// const labelSlot = newSlot(Symbol('label'))(newLiteral("number of sheep"));
|
||||
|
||||
// const combineFn = newLiteral(label => numberOfSheep => `${label}: ${numberOfSheep}`)
|
||||
|
||||
// // Slot<String>
|
||||
// const labelAndValueSlotA = overwrite(labelSlot)(
|
||||
// transform(read(numberOfSheepSlot))(
|
||||
// transform(read(labelSlot))(combineFn)));
|
||||
|
||||
// const labelAndValueSlotB = overwrite(labelSlot)(
|
||||
// transform(read(alternativeNumberOfSheepSlot))(
|
||||
// transform(read(labelSlot))(combineFn)));
|
||||
|
||||
// console.log(
|
||||
// add(add(emptySet(compareSlots(strCompare)))(labelAndValueSlotA))(labelAndValueSlotB)
|
||||
// );
|
||||
|
||||
// merge()(labelSlot)(labelAndValueSlot)
|
||||
|
||||
|
||||
console.log("#############")
|
||||
console.log("## RB Tree ##")
|
||||
console.log("#############")
|
||||
|
||||
// just a small experiment
|
||||
console.log(new RBTreeWrapper(createRBTree().insert(1).insert(1).insert(2)));
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
import { pretty } from "../util/pretty.js";
|
||||
import { deepEqual } from "../util/util.js";
|
||||
|
||||
// UUID -> Computation<a> -> Slot<a>
|
||||
const newSlot = uuid => computation => ({
|
||||
overwrites: uuid,
|
||||
computation,
|
||||
// depth: 1,
|
||||
});
|
||||
|
||||
// a -> Computation<a>
|
||||
const newValue = val => ({
|
||||
kind: "value",
|
||||
out: val,
|
||||
});
|
||||
|
||||
// Slot<a> -> Computation<a>
|
||||
const read = slot => ({
|
||||
kind: "read",
|
||||
slot,
|
||||
out: slot.computation.out,
|
||||
});
|
||||
|
||||
// Computation<a> -> Computation<a -> b> -> Computation<b>
|
||||
const transform = input => fn => {
|
||||
const output = fn.out(input.out);
|
||||
if (input.kind === "value") {
|
||||
// optimization: sandwich everything together
|
||||
return {
|
||||
kind: "value",
|
||||
out: output,
|
||||
};
|
||||
}
|
||||
else {
|
||||
return {
|
||||
kind: "transformation",
|
||||
in: input,
|
||||
fn,
|
||||
out: output,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
const verifyComputation = (computation, indent=0) => {
|
||||
const printIndent = (...args) => {
|
||||
console.log(" ".repeat(indent*2), ...args);
|
||||
}
|
||||
const compare = (a,b, kind) => {
|
||||
if (typeof a === 'function' && typeof b === 'function') {
|
||||
printIndent("cannot compare functions", `(${kind})`);
|
||||
}
|
||||
else if (deepEqual(a, b)) {
|
||||
printIndent(`ok (${kind})`);
|
||||
}
|
||||
else {
|
||||
printIndent(`bad (${kind})`);
|
||||
}
|
||||
}
|
||||
if (computation.kind === "value") {
|
||||
compare(1, 1, "value");
|
||||
}
|
||||
else if (computation.kind === "read") {
|
||||
compare(computation.out, computation.slot.computation.out, "read");
|
||||
}
|
||||
else if (computation.kind === "transformation") {
|
||||
compare(computation.fn.out(computation.in.out),
|
||||
computation.out, "transformation");
|
||||
|
||||
verifyComputation(computation.in, indent+1);
|
||||
verifyComputation(computation.fn, indent+1);
|
||||
}
|
||||
}
|
||||
|
||||
const getReadDependencies = computation => {
|
||||
if (computation.kind === "value") {
|
||||
return new Set();
|
||||
}
|
||||
else if (computation.kind === "read") {
|
||||
return new Set([computation.slot]);
|
||||
}
|
||||
else if (computation.kind === "transformation") {
|
||||
return new Set([
|
||||
...getReadDependencies(computation.in),
|
||||
...getReadDependencies(computation.fn),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const inc = x => x + 1;
|
||||
|
||||
// const counterOne = newSlot(Symbol('counter'))(newValue(1));
|
||||
// const valueOne = read(counterOne);
|
||||
// console.log(pretty({valueOne}));
|
||||
// const onePlusOne = transform(valueOne)(newValue(inc));
|
||||
// console.log(pretty({onePlusOne}));
|
||||
// const onePlusOnePlusOne = transform(onePlusOne)(newValue(inc));
|
||||
// console.log(pretty({onePlusOnePlusOne}));
|
||||
|
||||
// verifyComputation(valueOne);
|
||||
// verifyComputation(onePlusOne);
|
||||
// verifyComputation(onePlusOnePlusOne);
|
||||
|
||||
|
||||
const priceSlot = newSlot(Symbol('price'))(newValue(20.66));
|
||||
const taxSlot = newSlot(Symbol('tax'))(newValue(4.34));
|
||||
|
||||
console.log(pretty({price: priceSlot}));
|
||||
|
||||
const computeTotal = tax => {
|
||||
const addTax = price => price + tax;
|
||||
return addTax;
|
||||
};
|
||||
|
||||
const total =
|
||||
transform(read(priceSlot))(
|
||||
transform(read(taxSlot))(
|
||||
newValue(computeTotal)));
|
||||
|
||||
console.log(pretty({total}))
|
||||
|
||||
const totalPlusOne = transform(total)(newValue(inc));
|
||||
|
||||
console.log(pretty({totalPlusOne}));
|
||||
|
||||
verifyComputation(totalPlusOne);
|
||||
|
||||
|
||||
|
||||
console.log("getReadDependencies(totalPlusOne):", getReadDependencies(totalPlusOne));
|
||||
Loading…
Add table
Add a link
Reference in a new issue