edge-effects
NOTE: Mostly for educative and exploratory purposes for now.
Pluggable runtime 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 runtime/interpreter:
const createRuntime = ; /** * Creating a runtime from middleware, effects and io */const runtime = ; /** * 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 runtime */;
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 runtime to be able to resolve/handle.
/** * Signature */const effects = effectName: { /** * Return a description of your effect */ return type: '@@your-own-type' your own args ; } { /** * Handle the resolving of the effect * and call back the result */ ; }
Example:
const effects = /** * 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 */ call: { return type: '@@call' func args ; } { let result; let error; try result = ; catch e error = e; return error ? Promise : Promise ; } /** * 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: { return type: '@@put' action ; } { ; }
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 = ; /** * Runtime */const createRuntime = ; /** * 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 takeAction; call; putAction; } /** * A process we want to run * that communicates with another * process by putting actions into * the event loop and listening for actions */ { while true putAction; takeAction; call; } /** * A process that listens for * events on a stream and outputs * events to another stream */ { while true const data = takeStream; putStream; } /** * A process that communicates with * another process over a socket / emitter * via events */ { while true call; putEvent; const data = takeEvent; putStream; } /** * A process that communicates with * another process over a socket / emitter * via events */ { while true const data = ; putStream; call; putEvent; } /** * A process that waits for stdin * and outputs the data to stdout */ { while true const data = takeStream; putStream; } /** * 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 = race; /** * Cycle the delay table */ const last = delayTable; delayTable; call; } /** * 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 putStream; call; } /** * A process that waits for stdin * and outputs the data to stdout */ { while true const data = takeStream; ; } /** * A process that waits for stdin * and outputs the data to stdout */ { while true const data = takeStream; const chars = data; let currentChar; while currentChar = chars putStream; call; } /** * A process that waits for stdin * and outputs the data to stdout */ { fork; fork;} /** * 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 = parallel; /** * Cycle the delay table */ const last = delayTable; delayTable; /** * TODO: Implement apply effect * that handles calling methods * with correct this context */ call; } /** * 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 runtime */ { /** * 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 runtime 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 a runtime */ const runtime = ; /** * 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 */;