Getting started

Getting started

Install hlang, write a graph in code, then run the same program as data.

Install

Shell
npm install @hlang-org/runtime @hlang-org/interpreter

The runtime is CommonJS with type declarations; ES modules import its named exports. The other packages ship ES modules and CommonJS.

Write three nodes

A node is a class. It declares its ports in the constructor and does its work in one method: _read for a Readable, _transform for a Transform, _write for a Writable.

JavaScript
// Three nodes, one of each kind. Each declares its ports in the constructor.
import { ReadableNode, TransformNode, WriteableNode, Port } from '@hlang-org/runtime';

/** R: sends one greeting on `out`. */
export class Greeting extends ReadableNode {
  constructor(opts) {
    super(opts);
    Port.O('out').attach(this);
  }
  _read($o) {
    $o('out').send('hello, hlang');
  }
}

/** T: upper-cases every text it receives on `in` and sends it on `out`. */
export class Uppercase extends TransformNode {
  constructor(opts) {
    super(opts);
    Port.I('in').attach(this);
    Port.O('out').attach(this);
  }
  _transform($i, $o) {
    $i('in').receive(text => $o('out').send(text.toUpperCase()));
  }
}

/** W: prints every text it receives on `in`. */
export class Print extends WriteableNode {
  constructor(opts) {
    super(opts);
    Port.I('in').attach(this);
  }
  _write($i) {
    $i('in').receive(text => console.log(text));
  }
}

Wire them and run

Connect an output port to an input port, then run the flow from the Readable root.

JavaScript
import { Flow } from '@hlang-org/runtime';
import { Greeting, Uppercase, Print } from './lib/nodes.mjs';

const greeting = new Greeting();
const uppercase = new Uppercase();
const print = new Print();

// Wiring is always output to input.
greeting.O('out').connect(uppercase.I('in'));
uppercase.O('out').connect(print.I('in'));

// Run from the Readable root.
new Flow().run(greeting);
Output
HELLO, HLANG

Run the same program as data

The same graph as JSON. Each node's code names a node class; the registry maps codes to classes.

JavaScript
// The same program as data: an IGraph. A connection's Output is the upstream
// port and its Input is the downstream port.
export const graph = {
  nodeList: [
    { nodeId: 'a', code: 'greeting', name: 'Greeting', type: 'R' },
    { nodeId: 'b', code: 'uppercase', name: 'Uppercase', type: 'T' },
    { nodeId: 'c', code: 'print', name: 'Print', type: 'W' },
  ],
  connections: [
    { Output: { nodeId: 'a', portConf: { id: 'out' } }, Input: { nodeId: 'b', portConf: { id: 'in' } } },
    { Output: { nodeId: 'b', portConf: { id: 'out' } }, Input: { nodeId: 'c', portConf: { id: 'in' } } },
  ],
};
JavaScript
import { Flow } from '@hlang-org/runtime';
import { interpret, NodeRegistry } from '@hlang-org/interpreter';
import { Greeting, Uppercase, Print } from './lib/nodes.mjs';
import { graph } from './lib/graph.mjs';

// The registry maps each node `code` in the graph to a node class.
const registry = new NodeRegistry()
  .register('greeting', Greeting)
  .register('uppercase', Uppercase)
  .register('print', Print);

const handle = interpret(graph, { registry, createFlow: () => new Flow() });
handle.onError(({ nodeId, error }) => console.error(nodeId, error));

// Stop every node and release its subscriptions.
handle.dispose();
Output
HELLO, HLANG

Every example on this site is a file in the repository that runs in CI. The output above is the output of that run.

Next: Nodes and ports, or compile the graph to a module.

The examples on this page run in CI. Edit this page on GitHub