interprit
NOTE: Mostly for educative and exploratory purposes for now.
Pluggable interpreter and effects engine (think redux-saga, except you define your own set of effects and higher order effects).
See this talk for some of the motivations of building this (and pushing side effects to the edge).
Usage
Creating an effects interpreter/runtime:
const createInterpreter = ; /** * Creating an interpreter from middleware, effects and io */const interpreter = ; /** * Create a final handler that will be called when your process has completed */const finalHandler = { ... }; /** * Create a shared context that your processes might use as shared memory */const context = ... ; /** * Create an object of arguments that will be passed to your process */const args = ... ; /** * Create a process to run */const process = { ... }; /** * Run your process with the interpreter */;
IO:
Define the interface for:
- dispatching events into the system
- subscribing to events in the system
- getting the state of the system (optional)
const io = dispatch: storedispatch subscribe: subscribeToDispatchMiddlewaresubscribe getState: storegetState
Effects (descriptors + resolvers):
Creating a set of effects you want your interpreter to be able to resolve/handle.
/** * Effect Description Signature */const effectName = { /** * Return a description of your effect */ return type: '@@your-own-type' your own args ;} /** * Effect Resolver Signature * NOTE: Attached to the effect description signature (!) */effectName { /** * Handle the resolving of the effect * and call back the result */ ;} /** * Effects Object Signature */const effects = effectName;
Example:
/** * Create an effect bundle for calling * a function that returns a promise * or a value and might have side effects * * Handles an effect spec of the call type * which resolves both synchronous function * calls and function calls that returns a promise */const call = { return type: '@@call' func args ;}; call { let result; let error; try result = ; catch e error = e; return error ? Promise : Promise ;}; const put = { return type: '@@put' action ;}; /** * Create an effect bundle for putting * an action into the system * * Handle an effect spec of the put-action * type which resolves dispatching actions * into the io system */put { ;}; /** * Create an effects bundle that can be used by the engine */const effects = call put;
Examples
'use strict'; /** * Redux */const createStore applyMiddleware = ; /** * Redux middleware */const addDispatchSubscriptionToStore addLoggingToStore } = ; /** * Event emitter */const EventEmitter = ; /** * Effects */const call callProc cps race fork parallel putAction takeAction putStream takeStream putEvent takeEvent = ; /** * Utils */const delay = ; /** * Interpreter */const createInterpreter = ; /** * Middleware to add logging of effects */ { console; return effect;} /** * A process we want to run * that communicates with another * process by putting actions into * the event loop and listening for actions */ { while true ; ; ; } /** * A process we want to run * that communicates with another * process by putting actions into * the event loop and listening for actions */ { while true ; ; ; } /** * A process that listens for * events on a stream and outputs * events to another stream */ { while true const data = ; ; } /** * A process that communicates with * another process over a socket / emitter * via events */ { while true ; ; const data = ; ; } /** * A process that communicates with * another process over a socket / emitter * via events */ { while true const data = ; ; ; ; } /** * A process that waits for stdin * and outputs the data to stdout */ { while true const data = ; ; } /** * A process that races two async calls * and alternates who "wins" every turn */ { let delayTable = 200 500 1000 1500; while true /** * Race two async calls */ const data = ; /** * Cycle the delay table */ const last = delayTable; delayTable; ; } /** * A sub-process that writes a string to * stdout one character at the time with an interval */ { const chars = str; let char; while char = chars ; ; } /** * A process that waits for stdin * and outputs the data to stdout */ { while true const data = ; ; } /** * A process that waits for stdin * and outputs the data to stdout */ { while true const data = ; const chars = data; let currentChar; while currentChar = chars ; ; } /** * A process that waits for stdin * and outputs the data to stdout */ { ; ;} /** * A process that runs two races in parallel * and alternates who "wins" every turn */ { let delayTable = 200 500 1000 1500; while true /** * Perform two async races in parallel */ const data = ; /** * Cycle the delay table */ const last = delayTable; delayTable; /** * TODO: Implement apply effect * that handles calling methods * with correct this context */ ; } /** * Create a handler that will handle * the built up context of each program that is run */ { console;} /** * Create a state reducer function */ { } /** * Run the program using our interpreter */ { /** * Create instance of takeActionsMiddleware */ const subscribeToDispatchMiddleware = ; /** * Create instance of logger middleware */ const loggerMiddleware = ; /** * Application state handler */ const store = ; /** * Create subscriber for state changes */ store; /** * Create the IO interface to pass to * the interpreter for handling takeAction/putAction/select */ const io = dispatch: storedispatch subscribe: subscribeToDispatchMiddlewaresubscribe getState: storegetState /** * TODO: * * Create channels / emitters for * - input (key, stdin) * - events * - sockets * - streams * - what else..? * * CSP (Communicating Sequencial Processes) ? * * NOTE: * * - eventEmitters/sockets do not have buffering and are asynchronous * - csp channels have buffering and are "synchronous" (put will wait until message is taken) * */ const socket = ; /** * Create an interpreter */ const interpreter = ; /** * Gather all the processes */ const processes = processOne processTwo streamProcess socketProcessOne socketProcessTwo stdEchoProcess raceProcess parallelProcess slowEchoProcess slowPrintEcho ; /** * Arguments for each process, * dependencies * - channels * - emitters * - streams * - whatever is needed as injected dependencies */ const args = socket ; /** * Create a global context */ const context = {}; /** * Run all the processes */ processes;} /** * Start the application */;