Kalandra

Learn

Complete workflows

A functional language for parallel and distributed programming. The next chapter of Clio.

These are fresh Kalandra programs, not historical Clio compatibility claims. Their complete source lives in examples/workflows. Acceptance checks copy the projects outside the workspace, build and run them through both the source CLI and the isolated bundled CLI, and compare their results.

These examples use multiple files supplied in the Kalandra repository. Download the release source or check out the repository, then run the commands below from its root with the installed Kalandra CLI. The checkout supplies example programs and service configuration; you do not need to build the compiler. These are not single-file playground programs.

Install the CLI first

Get the example sources

Invoices: modules, inheritance and JavaScript

The invoice project imports a local JavaScript package with explicit Kalandra signatures. DiscountedItem takes its own discount first, then the inherited price and quantity. Pricing crosses the JavaScript boundary using a known Number signature. This is an arithmetic example, not a currency precision model.

bash
cd examples/workflows/invoices
bun install --offline --ignore-scripts
cd ../../..
kalandra build examples/workflows/invoices/main.kalandra --strict -o /tmp/kalandra-invoices
kalandra run examples/workflows/invoices/main.kalandra --strict

Expected result: [35.91, 18]. The JavaScript package is included under vendor, so installation needs no registry dependency.

types.kalandra

kalandra
export type Item:
  Number price
  Number quantity

export type DiscountedItem is Item:
  Number discount

pricing.kalandra

kalandra
from "./types" import DiscountedItem
from "esm:invoice-math" import roundMoney

@params DiscountedItem
@returns Number
export fn total item:
  roundMoney (item.price * item.quantity * (1 - item.discount))

main.kalandra

kalandra
from "./types" import DiscountedItem
from "./pricing" import total

export fn main:
  items = [(DiscountedItem 0.1 19.95 2) (DiscountedItem 0.25 8 3)]
  items -> * total

package.json

json
{
  "name": "kalandra-invoice-example",
  "private": true,
  "type": "module",
  "dependencies": {
    "invoice-math": "file:./vendor/invoice-math"
  }
}

vendor/invoice-math/index.js

javascript
export const roundMoney = (value) => Math.round(value * 100) / 100;

vendor/invoice-math/package.json

json
{
  "name": "invoice-math",
  "version": "1.0.0",
  "type": "module",
  "exports": "./index.js",
  "kalandra": {
    "types": "./kalandra.types.json"
  }
}

vendor/invoice-math/kalandra.types.json

json
{
  "version": 1,
  "modules": {
    "esm:.": {
      "exports": {
        "roundMoney": {
          "kind": "Callable",
          "parameters": ["Number"],
          "returns": "Number",
          "async": false
        }
      }
    }
  }
}

Local batch: nested workers and captures

Each mapped process task calls a nested calibrate task. The calibration record is a typed capture. Mutating its offset changes that invocation's snapshot only; both batches return the same calibrated values, and the original reading stays unchanged.

bash
kalandra run examples/workflows/local-batch/main.kalandra --strict

Expected result: [12, 22, 12, 22, 10]. The final 10 is the untouched input reading.

types.kalandra

kalandra
export type Reading:
  Number value
export type TaggedReading is Reading:
  String label

tasks.kalandra

kalandra
from "./types" import TaggedReading

type Calibration:
  Number offset
Calibration settings = Calibration 1

@params TaggedReading
@returns TaggedReading
fn calibrate reading:
  settings.offset = settings.offset + 1
  TaggedReading reading.label (reading.value + settings.offset)

@params TaggedReading
@returns TaggedReading
export fn process reading: await |calibrate| reading

main.kalandra

kalandra
from "./types" import TaggedReading
from "./tasks" import process

export fn main:
  readings = [(TaggedReading "north" 10) (TaggedReading "south" 20)]
  first = readings -> * [await] |process|
  second = readings -> * [await] |process|
  [first[0].value first[1].value second[0].value second[1].value readings[0].value]

Remote batch: mapping, recovery and cleanup

The service owns its captured calibration settings. A local client worker maps remote calls over TaggedReading records, then repeats the batch to check isolation. The contract generates client constructors without loading server code.

First build the service and copy its contract:

bash
kalandra build examples/workflows/remote-batch/service/main.kalandra --strict -o /tmp/kalandra-readings --service calibrate=calibrate --service broken=broken --service slow=slow
cp /tmp/kalandra-readings/kalandra.client.json examples/workflows/remote-batch/client/readings.client.json
export KALANDRA_REMOTE_TOKEN=$(bun -e 'console.log(crypto.randomUUID().replaceAll("-", ""))')
kalandra serve /tmp/kalandra-readings/kalandra.deployment.json --port 3000

In another terminal, set KALANDRA_REMOTE_TOKEN to the same value and run:

bash
kalandra run examples/workflows/remote-batch/client/main.kalandra --strict --bindings examples/workflows/remote-batch/client/endpoints.json

Expected result: [12, 22, 12, 22]. Keep the token in the environment; endpoints.json contains only the environment variable name. Rebuild and recopy the contract whenever the service changes.

To exercise failure cleanup, run client/failure.kalandra with the same bindings. It starts slow work and then fails deliberately. The CLI should exit with code 1 after cancelling its outstanding work. Run main.kalandra again to check recovery.

To exercise cancellation, run client/cancel.kalandra with the same bindings and press Ctrl+C while it is working. The client should exit with code 130 after cancellation is acknowledged. main.kalandra should still succeed. Stop the service with Ctrl+C when finished.

The automated network checks wait for an actual server worker before interrupting the client, verify the client and server worker processes exit, retry a successful batch after failures, and confirm service shutdown closes its listener. Signal and process checks run on Unix hosts.

service/main.kalandra

kalandra
export type Reading:
  Number value
export type TaggedReading is Reading:
  String label

type Calibration:
  Number offset
Calibration settings = Calibration 1

@params TaggedReading
@returns TaggedReading
export fn calibrate reading:
  settings.offset = settings.offset + 1
  TaggedReading reading.label (reading.value + settings.offset)

@params Number
@returns Number
export fn broken n:
  n.missing ()
  n

@params Number
@returns Number
export fn slow n:
  if n < 2: n
  else: (slow (n - 1)) + (slow (n - 2))

client/main.kalandra

kalandra
from "remote:./readings.client.json" import TaggedReading calibrate

@params Number
fn batch start:
  readings = [(TaggedReading "north" start) (TaggedReading "south" (start + 10))]
  readings -> * [await] calibrate

export fn main:
  first = await |batch| 10
  second = await |batch| 10
  [first[0].value first[1].value second[0].value second[1].value]

client/failure.kalandra

kalandra
from "remote:./readings.client.json" import broken slow

@params Number
fn work n:
  pending = slow 45
  await broken n

export fn main: await |work| 1

client/cancel.kalandra

kalandra
from "remote:./readings.client.json" import slow

@params Number
fn work n: await slow n

export fn main: await |work| 45

client/endpoints.json

json
{
  "version": 1,
  "endpoints": {
    "readings.client.json": {
      "url": "http://127.0.0.1:3000",
      "tokenEnv": "KALANDRA_REMOTE_TOKEN"
    }
  }
}

What this establishes

These workflows test composition of implemented features. They do not establish full Clio parity, production hosting readiness or browser parity. The remote example uses authenticated loopback HTTP; TLS and public deployment remain separate work. Any historical comparison must first demonstrate that the selected program runs under the pinned Clio implementation.

Use bun run check for local examples and isolated builds. Use bun run test:network and, after building packages, bun run test:network --built for remote examples. The network checks need permission to open local sockets.