purelogic-ts
Version:
PureLogic for TypeScript
394 lines (393 loc) • 12.8 kB
JavaScript
"use strict";
require("tslib");
const iterable = require("iterable-ts");
class CacheMap {
constructor() {
this._map = {};
}
set(id, value) {
this._map[id] = value;
}
get(id, create) {
const result = this._map[id];
if (result === undefined) {
const newResult = create();
this._map[id] = newResult;
return newResult;
}
return result;
}
}
exports.CacheMap = CacheMap;
/**
* Bag type and related functions.
*/
var bag;
(function (bag_1) {
class FlatMap {
constructor(input, func) {
this.input = input;
this.func = func;
}
}
bag_1.FlatMap = FlatMap;
class DisjointUnion {
constructor(a, b) {
this.a = a;
this.b = b;
}
}
bag_1.DisjointUnion = DisjointUnion;
class GroupBy {
constructor(input, toKey, reduce) {
this.input = input;
this.toKey = toKey;
this.reduce = reduce;
}
}
bag_1.GroupBy = GroupBy;
class Product {
constructor(a, b, func) {
this.a = a;
this.b = b;
this.func = func;
}
}
bag_1.Product = Product;
class Dif {
constructor(value, a, b) {
this.value = value;
this.a = a;
this.b = b;
}
}
bag_1.Dif = Dif;
function one(value) {
return new Bag((visitor) => visitor.one(value));
}
bag_1.one = one;
function input() {
return new Bag((visitor) => visitor.input());
}
bag_1.input = input;
function range(a, b) {
return one(null).flatMap(() => iterable.range(a, b));
}
bag_1.range = range;
let bagCounter = 0;
class Join {
constructor(key, a, b) {
this.key = key;
this.a = a;
this.b = b;
}
}
bag_1.Join = Join;
class Bag {
constructor(implementation) {
this.implementation = implementation;
this.id = bagCounter.toString();
++bagCounter;
}
/**
* LINQ: SelectMany
*/
flatMap(func) {
return new Bag((visitor) => visitor.flatMap(new FlatMap(this, func)));
}
disjointUnion(b) {
return new Bag((visitor) => visitor.disjointUnion(new DisjointUnion(this, b)));
}
/**
* LINQ: GroupBy
*/
groupBy(toKey, reduce) {
return new Bag((visitor) => visitor.groupBy(new GroupBy(this, toKey, reduce)));
}
product(b, func) {
return new Bag((visitor) => visitor.product(new Product(this, b, func)));
}
/**
* LINQ: Select
*/
map(func) {
return this.flatMap(value => [func(value)]);
}
/**
* LINQ: Where
*/
filter(func) {
return this.flatMap(iterable.filterFuncToFlatMapFunc(func));
}
compact() {
return this.filter(Boolean);
}
/**
* LINQ: Accumulate
*/
reduce(func) {
return this.groupBy(() => "", func);
}
dif(b) {
const toDif = (bag, a, b) => bag.map(v => new Dif(v, a, b));
const aDif = toDif(this, 1, 0);
const bDif = toDif(b, 0, 1);
return aDif.disjointUnion(bDif).groupBy(v => JSON.stringify(v.value), (x, y) => new Dif(x.value, x.a + y.a, x.b + y.b));
}
join(b, keyT, keyB, reduceT, reduceB) {
function join(k, t, b) {
return new Join(k, t, b);
}
const bagT = this.map(x => join(keyT(x), x, undefined));
const bagB = b.map(x => join(keyB(x), undefined, x));
const bagC = bagT.disjointUnion(bagB);
function reduceOptional(reduce) {
return (x, y) => {
if (x === undefined) {
return y;
}
if (y === undefined) {
return x;
}
return reduce(x, y);
};
}
return bagC.groupBy(x => x.key, (x, y) => join(x.key, reduceOptional(reduceT)(x.a, y.a), reduceOptional(reduceB)(x.b, y.b)));
}
}
bag_1.Bag = Bag;
})(bag = exports.bag || (exports.bag = {}));
/**
* Optimized graph.
*/
var optimized;
(function (optimized) {
class Node {
constructor(id, implementation) {
this.id = id;
this.implementation = implementation;
}
link(func) {
const value = new LinkValue(this, func);
return new Link((visitor) => visitor(value));
}
bag() {
return new Bag(this.id, [this.link(iterable.flatMapIdentity)]);
}
}
optimized.Node = Node;
class LinkValue {
constructor(node, func) {
this.node = node;
this.func = func;
}
}
optimized.LinkValue = LinkValue;
class Link {
constructor(implementation) {
this.implementation = implementation;
}
nodeId() {
return this.implementation((x) => x.node.id);
}
flatMap(func) {
function visitor(x) {
const f = x.func;
const newFunc = f !== iterable.flatMapIdentity
? (value) => iterable.toArray(iterable.flatMap(f(value), func))
: func;
return x.node.link(newFunc);
}
return this.implementation(visitor);
}
addFunc(getFunc) {
function visitor(link) {
const f = link.func;
const fNew = getFunc();
return link.node.link(i => iterable.concat(f(i), fNew(i)));
}
return this.implementation(visitor);
}
}
optimized.Link = Link;
class Bag {
constructor(id, links) {
this.id = id;
this.links = links;
}
linksMap(visitor) {
return this.links.map(link => link.implementation(visitor));
}
groupBy(id, toKey, reduce) {
return new Node(id, (visitor) => visitor.groupBy(this, toKey, reduce))
.bag();
}
product(id, b, func) {
return new Node(id, (visitor) => visitor.product(this, b, func))
.bag();
}
flatMap(id, func) {
return new Bag(id, this.links.map(link => link.flatMap(func)));
}
disjointUnion(id, b) {
const c = iterable.concat(this.links, b.links);
const g = iterable.groupBy(c, x => x.nodeId(), (x, y) => {
function visitor(v) {
function getFunc() { return v.func; }
return x.addFunc(getFunc);
}
return y.implementation(visitor);
});
return new Bag(id, iterable.toArray(iterable.values(g)));
}
}
optimized.Bag = Bag;
function input(id) {
return new Node(id, (visitor) => visitor.input()).bag();
}
optimized.input = input;
function one(id, value) {
return new Node(id, (visitor) => visitor.one(value)).bag();
}
optimized.one = one;
/**
* DAG
*/
class Dag {
constructor() {
this._map = new CacheMap();
}
get(b) {
const id = b.id;
return this._map.get(id, () => {
const getOpimized = (b) => this.get(b);
class Visitor {
flatMap(value) {
return getOpimized(value.input).flatMap(id, value.func);
}
disjointUnion(value) {
return getOpimized(value.a).disjointUnion(id, getOpimized(value.b));
}
one(value) {
return optimized.one(id, value);
}
input() {
return optimized.input(id);
}
groupBy(value) {
return getOpimized(value.input).groupBy(id, value.toKey, value.reduce);
}
product(value) {
return getOpimized(value.a).product(id, getOpimized(value.b), value.func);
}
}
return b.implementation(new Visitor());
});
}
}
optimized.Dag = Dag;
})(optimized = exports.optimized || (exports.optimized = {}));
class InputError {
constructor(bagId) {
this.bagId = bagId;
this.name = "InputError";
this.message = `InputError: input bag ${bagId} is not defined`;
}
}
exports.InputError = InputError;
class Mem {
constructor() {
this._map = new CacheMap();
this._dag = new optimized.Dag();
}
}
exports.Mem = Mem;
/**
* Synchronous memory back-end.
*/
class SyncMem extends Mem {
set(input, factory) {
this._map.set(input.id, factory);
}
get(b) {
return this._get(this._dag.get(b));
}
_get(o) {
const id = o.id;
return this._map.get(id, () => {
// NOTE: possible optimization:
// if (value.func === flatMap.identity) { return nodeFunc; }
const links = o.linksMap((value) => iterable.flatMap(this._fromNode(value.node), value.func));
// NOTE: possible optimization: if (links.lenght === 1) { newResult = links[0]; }
return iterable.flatten(links);
});
}
_fromNode(n) {
const id = n.id;
const map = this._map;
return map.get(id, () => {
const get = (b) => this._get(b);
class Visitor {
/**
* when input is not defined yet.
*/
input() { throw new InputError(id); }
one(value) { return [value]; }
groupBy(input, toKey, reduce) {
return iterable.values(iterable.groupBy(get(input), toKey, reduce));
}
product(a, b, func) {
return iterable.product(get(a), get(b), func);
}
}
return n.implementation(new Visitor());
});
}
}
exports.SyncMem = SyncMem;
class AsyncMem extends Mem {
set(input, getArray) {
this._map.set(input.id, getArray);
}
get(b) {
return this._get(this._dag.get(b));
}
_get(o) {
const id = o.id;
return this._map.get(id, () => __awaiter(this, void 0, void 0, function* () {
// NOTE: possible optimization:
// if (value.func === flatMap.identity) { return nodeFunc; }
const linkPromises = o.linksMap((value) => __awaiter(this, void 0, void 0, function* () {
return iterable.flatMap(yield this._fromNode(value.node), value.func);
}));
// NOTE: possible optimization: if (links.lenght === 1) { newResult = links[0]; }
return iterable.flatten(yield Promise.all(linkPromises));
}));
}
_fromNode(n) {
const id = n.id;
const map = this._map;
return map.get(id, () => {
const get = (b) => this._get(b);
class Visitor {
input() { return Promise.reject(new InputError(id)); }
one(value) {
return __awaiter(this, void 0, void 0, function* () { return [value]; });
}
groupBy(input, toKey, reduce) {
return __awaiter(this, void 0, void 0, function* () {
const i = yield get(input);
return iterable.values(yield iterable.async.groupBy(i, toKey, reduce));
});
}
product(a, b, func) {
return __awaiter(this, void 0, void 0, function* () {
return iterable.product(yield get(a), yield get(b), func);
});
}
}
return n.implementation(new Visitor());
});
}
}
exports.AsyncMem = AsyncMem;