We're rapidly approaching a world where flow-based programming is taking the lead, but there isn't any useful notation to represent all of the complexities of flow management. Let's assume a basic flow that takes a click event, extracts the X and Y coordinates from it, and moves a player based on the extracted values.
+----------+ +-----------+ +------------+
| getClick +-----> extractXY +---------------> movePlayer |
+----------+ +-----------+ xyChannel +------+-----+
|
|
|RangeError
|
|
+----------v---------+
| displayRangeError |
+--------------------+
If this was a modern JavaScript function, it would look something like this:
const retry = require('async-retry')
const events = require('events').EventEmitter;
const emitter = new events.EventEmitter();
async main () {
document.on('click', (e) => {
emitter.emit('getClick', e)
const { x, y } = await extractXY(await xyChannel(e))
retry(async (bail, retried) => {
if (retried === 3) {
await logFailedClick()
return bail()
}
try {
const moveResults = await movePlayer(x, y)
emitter.emit('movePlayer', moveResults)
} catch (err) {
if (err instanceof RangeError) {
await displayRangeError(err)
emitter.emit('displayRangeError', err)
}
}
}, {
retries: 3
})
})
}
main()
As you can tell, async/await
control flow adds a level of complexity that isn't so intuitive to reason about. With FlowNote, we can generate this entire block of code with one line of code:
getClick -> extractXY* xyChannel{ retry: 3, onFail: logFailedClick }> movePlayer RangeError! displayRangeError
The grammar.ohm file is below, you copy and paste it to https://ohmlang.github.io/editor/
- Flow assignment and reuse.
- Node/flow factories or jumping flow pathing.
- Static flow analysis with path isolation.
- Node and channel tagging for easier targeting.
- Event dispatching based on node, flow assignment, and metanode completion.
- Step-based state transformation and replayability
- Flow reversal (TBD)