git.schokokeks.org
Repositories
Help
Report an Issue
fs-words.git
Code
Commits
Branches
Tags
Suche
Strukturansicht:
faf7e12
Branches
Tags
develop-client_server
master
typescript
fs-words.git
server
lib
plankton
call
logic-impl.js
[mov] server
Christian Fraß
commited
faf7e12
at 2021-03-08 23:05:42
logic-impl.js
Blame
History
Raw
/* This file is part of »bacterio-plankton:call«. Copyright 2016-2018 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' <info@greenscale.de> »bacterio-plankton:call« is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. »bacterio-plankton:call« is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with »bacterio-plankton:call«. If not, see <http://www.gnu.org/licenses/>. */ "use strict"; /* This file is part of »bacterio-plankton:call«. Copyright 2016-2018 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' <info@greenscale.de> »bacterio-plankton:call« is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. »bacterio-plankton:call« is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with »bacterio-plankton:call«. If not, see <http://www.gnu.org/licenses/>. */ var lib_call; (function (lib_call) { /** * @desc hacked class for postfix function application * @author fenris */ class class_valuewrapper { /** * @desc [constructor] * @author fenris */ constructor(value) { this.value = value; } /** * @desc [accessor] applies a function and returns a new valuewrapper * @author fenris */ pass(function_) { return (new class_valuewrapper(function_(this.value))); } /** * @desc [accessor] gives the wrapped value * @author fenris */ extract() { return this.value; } } lib_call.class_valuewrapper = class_valuewrapper; /** * @desc shortcut for constructing a valuewrapper-object * @author fenris */ function vw(value) { return (new class_valuewrapper(value)); } lib_call.vw = vw; /** * @author fenris */ function use(input, function_) { return function_(input); } lib_call.use = use; /** * @desc just the identity; useful for some callbacks etc. * @author fenris */ function id(x) { return x; } lib_call.id = id; /** * @desc composes two functions (i.e. returns a function that return the result of the successive execution of both input-functions) * @param {function} function_f * @param {function} function_g * @author fenris */ function compose(function_f, function_g) { return (function (x) { // return function_g(function_f(x)); return function_g(function_f.apply(function_f, lib_call.args2list(arguments))); }); } lib_call.compose = compose; /** * @desc transforms a function with sequential input into a function with leveled input; example: add(2,3) = curryfy(add)(2)(3) * @param {function} f * @param {int} n (don't set manually) * @return {function} the currified version of the in put function * @author fenris */ function curryfy(f, n = f.length) { switch (n) { case 0: { throw (new Error("[curryfy] impossible")); // break; } case 1: { return f; // break; } default: { return (function (x) { return (curryfy(function () { return f.apply(f, [x].concat(lib_call.args2list(arguments))); }, n - 1)); }); // break; } } } lib_call.curryfy = curryfy; })(lib_call || (lib_call = {})); var lib_call; (function (lib_call) { /** * @author fenris */ function executor_resolve(result) { return ((resolve, reject) => resolve(result)); } lib_call.executor_resolve = executor_resolve; /** * @author fenris */ function executor_reject(reason) { return ((resolve, reject) => reject(reason)); } lib_call.executor_reject = executor_reject; /** * @author fenris */ function executor_transform(executor, transform_result, transform_reason) { return ((resolve, reject) => { executor(result => resolve(transform_result(result)), reason => reject(transform_reason(reason))); }); } lib_call.executor_transform = executor_transform; /** * @author fenris */ function executor_transform_default(executor, transform_result, wrap_string = null) { let transform_reason = (error => ((wrap_string == null) ? error : new class_error(wrap_string, [error]))); return (executor_transform(executor, transform_result, transform_reason)); } lib_call.executor_transform_default = executor_transform_default; /** * @author fenris */ function executor_compose_sequential(first, second) { return ((resolve, reject) => { first(result => { second(result)(resolve, reject); }, reason => { reject(reason); }); }); } lib_call.executor_compose_sequential = executor_compose_sequential; /** * @author fenris */ function executor_chain(state, executors) { return ((resolve, reject) => { if (executors.length == 0) { return resolve(state); } else { return executors[0](state)(result => { executor_chain(result, executors.slice(1))(resolve, reject); }, reject); } }); /* */ /* if (executors.length == 0) { return executor_resolve<type_state, type_error>(state); } else if (executors.length == 1) { return executors[0](state); } else { return ( executor_chain<type_state, type_error>( state, [ state => (resolve, reject) => executors[0](state)(result => executors[1](result)(resolve, reject), reject) ].concat(executors.slice(2)) ) ); } */ /* return ( executors.reduce( (chain, current) => executor_compose_sequential<type_state, type_state, type_error>(chain, current, deferred), executor_resolve<type_state, type_error>(state) ) ); */ } lib_call.executor_chain = executor_chain; /** * @author fenris */ function executor_first(executors) { /* return ( (resolve, reject) => { if (executors.length == 0) { reject(new Error("all failed")); } else { executors[0]( result => { resolve(result); }, reason => { executor_first<type_result, type_reason>(executors.slice(1))(resolve, reject); } ) } } ); */ return ((resolve, reject) => { executor_chain([], executors.map(executor => reasons => (resolve_, reject_) => { executor(result => reject_(result), reason => resolve_(reasons.concat([reason]))); }))(errors => reject(errors), result => resolve(result)); }); } lib_call.executor_first = executor_first; /** * @author fenris */ function executor_condense(executors) { return (executor_chain([], executors.map(executor => result => (resolve, reject) => { executor(element => resolve(result.concat([element])), reject); }))); } lib_call.executor_condense = executor_condense; /** * @author fenris * @deprecated use condense */ function executor_filter(executors, predicate) { return (executor_chain([], executors.map(executor => result => (resolve, reject) => { executor(element => resolve(predicate(element) ? result.concat([element]) : result), reject); }))); } lib_call.executor_filter = executor_filter; /** * @author fenris * @deprecated use condense */ function executor_map(executors, transformator) { return (executor_chain([], executors.map(executor => result => (resolve, reject) => { executor(element1 => resolve(result.concat([transformator(element1)])), reject); }))); } lib_call.executor_map = executor_map; /** * @author fenris * @deprecated use condense */ function executor_reduce(executors, initial, accumulator) { return (executor_chain(initial, executors.map(executor => result => (resolve, reject) => { executor(element => resolve(accumulator(result, element)), reject); }))); } lib_call.executor_reduce = executor_reduce; })(lib_call || (lib_call = {})); /* This file is part of »bacterio-plankton:call«. Copyright 2016-2018 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' <info@greenscale.de> »bacterio-plankton:call« is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. »bacterio-plankton:call« is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with »bacterio-plankton:call«. If not, see <http://www.gnu.org/licenses/>. */ var lib_call; (function (lib_call) { /** * @author fenris */ function promise_reject(reason) { return Promise.reject(reason); } lib_call.promise_reject = promise_reject; /** * @author fenris */ function promise_resolve(result) { return Promise.resolve(result); } lib_call.promise_resolve = promise_resolve; /** * @author fenris */ function promise_make(executor) { return (new Promise(executor)); } lib_call.promise_make = promise_make; /** * @author fenris */ function promise_then_close(promise, resolver, rejector) { promise.then(resolver, rejector); } lib_call.promise_then_close = promise_then_close; /** * @author fenris */ function promise_then_append(promise, resolver, rejector = null) { if (rejector == null) { rejector = (reason) => promise_reject(reason); } return (promise.then(resolver, rejector)); } lib_call.promise_then_append = promise_then_append; /** * @author fenris */ function promise_all(promises) { return Promise.all(promises); } lib_call.promise_all = promise_all; /** * @author fenris */ function promise_chain(promises, start = undefined) { return (promises.reduce((chain, promise) => promise_then_append(chain, promise), promise_resolve(start))); } lib_call.promise_chain = promise_chain; /** * @author fenris */ function promise_condense(promises) { return (promise_chain(promises.map(promise => result => promise_then_append(promise(), element => promise_resolve(result.concat([element])))), [])); } lib_call.promise_condense = promise_condense; /** * @author fenris */ function promise_group(promises, serial = false) { const decorate = function (promise, name) { return (() => promise_then_append(promise(), value => promise_resolve({ "key": name, "value": value }))); }; const convert = function (array) { let object = {}; array.forEach(({ "key": key, "value": value }) => { object[key] = value; }); return object; }; if (serial) { return (promise_then_append(promise_condense(Object.keys(promises) .map(name => decorate(promises[name], name))), list => promise_resolve(convert(list)))); } else { return (promise_then_append(promise_all(Object.keys(promises) .map(name => decorate(promises[name], name)) .map(promise => promise())), list => promise_resolve(convert(list)))); } } lib_call.promise_group = promise_group; /** * @author fenris */ function promise_wrap(promise, transformator_result, transformator_reason = lib_call.id) { return (promise_make((resolve, reject) => { promise_then_close(promise, result => resolve(transformator_result(result)), reason => reject(transformator_reason(reason))); })); } lib_call.promise_wrap = promise_wrap; /** * @author fenris */ function promise_show(label) { return (result => promise_make((resolve, reject) => { lib_log.info(label + ": " + instance_show(result)); resolve(result); })); } lib_call.promise_show = promise_show; /** * @author fenris */ function promise_log(result) { return promise_show("log"); } lib_call.promise_log = promise_log; /** * @author fenris */ function promise_attach(state, promise, name) { return (promise_wrap(promise, result => { state[name] = result; return state; })); } lib_call.promise_attach = promise_attach; /** * @author fenris */ function promise_delay(promise, delay) { return promise_make((resolve, reject) => { lib_call.timeout(() => { promise_then_close(promise, resolve, reject); return null; }, delay); }); } lib_call.promise_delay = promise_delay; /** * @author fenris */ function promise_to_executor(promise) { return ((resolve, reject) => promise.then(resolve, reject)); } lib_call.promise_to_executor = promise_to_executor; })(lib_call || (lib_call = {})); /* This file is part of »bacterio-plankton:call«. Copyright 2016-2018 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' <info@greenscale.de> »bacterio-plankton:call« is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. »bacterio-plankton:call« is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with »bacterio-plankton:call«. If not, see <http://www.gnu.org/licenses/>. */ var lib_call; (function (lib_call) { lib_call.initializer_state_initial = 0; lib_call.initializer_state_waiting = 1; lib_call.initializer_state_successful = 2; lib_call.initializer_state_failed = 3; /** * @author fenris */ function initializer_make(fetcher) { let subject = { "fetcher": fetcher, "state": lib_call.initializer_state_initial, "queue": [], "result": undefined, "reason": undefined, }; return subject; } lib_call.initializer_make = initializer_make; /** * @author fenris */ function initializer_actuate(subject) { switch (subject.state) { case lib_call.initializer_state_successful: { subject.queue.forEach(entry => entry.resolve(subject.result)); break; } case lib_call.initializer_state_failed: { subject.queue.forEach(entry => entry.reject(subject.reason)); break; } default: { let message = `unhandled state ${subject.state}`; throw (new Error(message)); break; } } } /** * @author fenris */ function initializer_reset(subject) { subject.state = lib_call.initializer_state_initial; subject.queue = []; } lib_call.initializer_reset = initializer_reset; /** * @author fenris */ function initializer_state(subject) { return subject.state; } lib_call.initializer_state = initializer_state; /** * @author fenris */ function initializer_get(subject) { switch (subject.state) { case lib_call.initializer_state_initial: { subject.state = lib_call.initializer_state_waiting; return (lib_call.promise_make((resolve, reject) => { subject.queue.push({ "resolve": resolve, "reject": reject }); subject.fetcher().then(result => { subject.state = lib_call.initializer_state_successful; subject.result = result; initializer_actuate(subject); }, reason => { subject.state = lib_call.initializer_state_failed; subject.reason = reason; initializer_actuate(subject); }); })); break; } case lib_call.initializer_state_waiting: { return (lib_call.promise_make((resolve, reject) => { subject.queue.push({ "resolve": resolve, "reject": reject }); })); break; } case lib_call.initializer_state_successful: { return (lib_call.promise_resolve(subject.result)); break; } case lib_call.initializer_state_failed: { return (lib_call.promise_reject(subject.reason)); break; } default: { let message = `unhandled state ${subject.state}`; throw (new Error(message)); break; } } } lib_call.initializer_get = initializer_get; /** * @author fenris */ function initializer_get_sync(subject) { switch (subject.state) { case lib_call.initializer_state_successful: { return subject.result; break; } case lib_call.initializer_state_failed: { throw subject.reason; break; } default: { let message = `unhandled state ${subject.state}`; throw (new Error(message)); break; } } } /** * @author fenris */ function initializer_set_sync(subject, result) { switch (subject.state) { case lib_call.initializer_state_successful: { subject.result = result; break; } case lib_call.initializer_state_failed: { subject.state = lib_call.initializer_state_successful; subject.result = result; break; } default: { let message = `unhandled state ${subject.state}`; throw (new Error(message)); break; } } } })(lib_call || (lib_call = {})); /* This file is part of »bacterio-plankton:call«. Copyright 2016-2018 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' <info@greenscale.de> »bacterio-plankton:call« is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. »bacterio-plankton:call« is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with »bacterio-plankton:call«. If not, see <http://www.gnu.org/licenses/>. */ var lib_call; (function (lib_call) { /* The core idea of this library is to provide means for asynchronous program flow. The old-school way to do is, is to use callbacks. While this approach is simple and easy to understand, it has some disadvantages. As an attempt to relief and improve this, the promise-system was introduced. In principle it solves most of the problems found in the callback-approach; however it has some downsides as well: - Convolution of multiple principles Promises unite the ideas of asynchronous program flow and error handling. - Instant execution Creating a promise results in the instant execution of the given executor prodecure. While this might be convenient in some cases, it can be quite disturbing and counter-intuitive in others. - Broken typing The Promise system doesn't distinguish between an appending "then" (i.e. passing a function, which returns a new promise) and a closing "then" (i.e. passing a function, which has no return value). On top of that it allows returning simple values in an appending "then", which results in an implicit call of the executors "resolve"-function. The price for these "pragmatic" features is that the whole system can't be typed well. And even though JavaScript is not a strictly typed language, it was a quite questionable decision to design the promise system in a way, which breaks typing from the start. The deferral-system forseeks to solve these issues while retaining the advantages of the promise-system. */ /** * @author fenris * @desc activates the deferral and handles its output according to a given procedure * @param {(value : type_value)=>void} procedure a function which receives the output of the deferral as argument */ function deferral_use(deferral, input, procedure) { deferral.representation(input).then(value => { procedure(value); }, reason => { throw reason; }); } lib_call.deferral_use = deferral_use; /** * @author fenris * @desc creates a deferral-subject (similar to "new Promise", where "convey" reflects "resolve"/"reject") */ function deferral_make(handler) { return ({ "representation": ((input) => (new Promise((resolve, reject) => { handler(input, resolve); }))) }); } lib_call.deferral_make = deferral_make; /** * @author fenris * @desc wraps a simple function into a deferral (similar to "Promise.resolve"/"Promise.reject") */ function deferral_wrap(function_) { return (deferral_make((input, convey) => convey(function_(input)))); } lib_call.deferral_wrap = deferral_wrap; /** * @author fenris */ function deferral_id() { return (deferral_make((input, convey) => convey(input))); } lib_call.deferral_id = deferral_id; /** * @author fenris */ function deferral_const(value) { return (deferral_make((input, convey) => convey(value))); } lib_call.deferral_const = deferral_const; /** * @author fenris */ function deferral_delay(output, delay) { return (deferral_make((input, convey) => { setTimeout(() => convey(output), delay); })); } lib_call.deferral_delay = deferral_delay; /** * @author fenris * @desc connects two deferrals to form a new one; the output of the first is taken as input for the second * (similar to "Promise.then" when passing a function which returns a new promise) * @param {type_deferral<type_value1>} first a simple deferral * @param {(value1 : type_value1)=>type_deferral<type_value2>} second a function depending from a value returning a deferral */ function deferral_compose_serial(first, second) { return { "representation": ((input) => first.representation(input).then((between) => second.representation(between))) }; } lib_call.deferral_compose_serial = deferral_compose_serial; /** * @author fenris */ function deferral_compose_parallel({ "left": deferral_left, "right": deferral_right, }) { return (deferral_make((input, convey) => { let object = { "left": lib_maybe.make_nothing(), "right": lib_maybe.make_nothing(), }; let finish = function () { if (lib_maybe.is_just(object.left) && lib_maybe.is_just(object.right)) { let result = { "left": lib_maybe.cull(object.left), "right": lib_maybe.cull(object.right), }; convey(result); } else { // do nothing } }; deferral_use(deferral_left, input, output_left => { object.left = lib_maybe.make_just(output_left); finish(); }); deferral_use(deferral_right, input, output_right => { object.right = lib_maybe.make_just(output_right); finish(); }); })); } lib_call.deferral_compose_parallel = deferral_compose_parallel; /** * @author fenris * @desc repeatedly applied serial composition */ function deferral_chain(members) { return (members.reduce( // (result, current) => deferral_compose_serial<type_value, type_value, type_value>(result, current), deferral_compose_serial, deferral_id())); } lib_call.deferral_chain = deferral_chain; /** * @author fenris */ /* export function deferral_bunch<type_input, type_output>( members : {[name : string] : type_deferral<type_input, type_output>} ) : type_deferral<type_input, {[name : string] : type_output}> { } */ })(lib_call || (lib_call = {})); /* This file is part of »bacterio-plankton:call«. Copyright 2016-2018 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' <info@greenscale.de> »bacterio-plankton:call« is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. »bacterio-plankton:call« is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with »bacterio-plankton:call«. If not, see <http://www.gnu.org/licenses/>. */ var lib_call; (function (lib_call) { /** * @author fenris */ class class_deferral { /** * @author fenris */ constructor(subject) { this.subject = subject; } /** * @author fenris */ static _cram(subject) { return (new class_deferral(subject)); } /** * @author fenris */ static _tear(instance) { return instance.subject; } /** * @author fenris */ static make(handler) { return (class_deferral._cram(lib_call.deferral_make(handler))); } /** * @author fenris */ use(input, procedure) { return (lib_call.deferral_use(class_deferral._tear(this), input, procedure)); } /** * @author fenris */ compose_serial(second) { return (class_deferral._cram(lib_call.deferral_compose_serial(class_deferral._tear(this), class_deferral._tear(second)))); } /** * @author fenris */ static chain(members) { return (class_deferral._cram(lib_call.deferral_chain(members.map(member => class_deferral._tear(member))))); } /** * @author fenris */ static wrap(function_) { return (class_deferral._cram(lib_call.deferral_wrap(function_))); } /** * @author fenris */ static const_(value) { return (class_deferral._cram(lib_call.deferral_const(value))); } /** * @author fenris */ static delay(output, delay) { return (class_deferral._cram(lib_call.deferral_delay(output, delay))); } } lib_call.class_deferral = class_deferral; })(lib_call || (lib_call = {})); /* This file is part of »bacterio-plankton:call«. Copyright 2016-2018 'Christian Fraß, Christian Neubauer, Martin Springwald GbR' <info@greenscale.de> »bacterio-plankton:call« is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. »bacterio-plankton:call« is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with »bacterio-plankton:call«. If not, see <http://www.gnu.org/licenses/>. */ var lib_call; (function (lib_call) { /** * @author fenris */ function timeout(function_, delay) { return ( /*window.*/ setTimeout(function_, delay)); } lib_call.timeout = timeout; /** * @desc a definition for a value being "defined" * @author neuc */ function is_def(obj, null_is_valid = false) { return (!((typeof (obj) === "undefined") || (!null_is_valid && (obj === null)))); } lib_call.is_def = is_def; /** * @desc returns the value if set and, when a type is specified, if the type is correct, if not return default_value * @author neuc */ function def_val(value, default_value, type = null, null_is_valid = false) { if (is_def(value, null_is_valid) && (is_def(type) ? ((typeof value === type) || ((value === null) && null_is_valid)) : true)) { return value; } else { return default_value; } } lib_call.def_val = def_val; ; /** * @desc just the empty function; useful for some callbacks etc. * @author fenris */ function nothing() { } lib_call.nothing = nothing; /** * @desc outputs * @author fenris */ function output(...args) { lib_log.info.apply(lib_log, args); } lib_call.output = output; /** * @desc converts the "arguments"-map into an array * @param {Object} args * @author fenris */ function args2list(args) { return Object.keys(args).map(key => args[key]); } lib_call.args2list = args2list; /** * @desc provides the call for an attribute of a class as a regular function * @param {string} name the name of the attribute * @return {*} * @author fenris */ function attribute(name) { return ((object) => object[name]); } lib_call.attribute = attribute; /** * @desc provides a method of a class as a regular function * @param {string} name the name of the method * @return {function} * @author fenris */ function method(name) { return (function (object) { return object[name].apply(object, args2list(arguments).slice(1)); }); } lib_call.method = method; /** * @author fenris */ function distinguish(unival, handlers, fallback = null) { if (unival.kind in handlers) { let handler = handlers[unival.kind]; return handler(unival.data); } else { let message = ("unhandled kind '" + unival.kind + "'"); if (fallback !== null) { console.warn(message); return fallback(unival); } else { throw (new Error(message)); } } } lib_call.distinguish = distinguish; })(lib_call || (lib_call = {}));