Concurrency

As of version v0.4, Hachi includes first-phase concurrency support for transpiled builds.

The model is intentionally lightweight and Go-inspired, centered around:

  • go: for spawning work asynchronously
  • Channel<T> for typed message passing
  • direct channel operations such as makeChannel, send, recv, and close
  • non-blocking, timeout-aware, and select-style receive/send helpers

This page covers the currently supported concurrency features and how to use them.

Overview

Hachi concurrency in v0.4 is built around explicit task spawning and typed channels.

A typical flow looks like this:

  • create a channel with makeChannel
  • start one or more workers with go:
  • send values into or out of channels
  • receive values from channels
  • close channels when finished

Spawning Tasks with go:

Use go: to start a function asynchronously.

ping :: {} -> {Void}:(
    print: "hello from a task"
)

go: ping

You can also pass arguments:

worker :: {Int} -> {Void}:(
    print: Ri.a.String
)

go: worker: 42

You can pass multiple arguments as well:

producer :: {Channel<Int>, Int} -> {Void}:(
    send: Ri.a, Ri.b
)

ch: makeChannel: Int
go: producer: ch, 99
print: recv: ch
close: ch

Supported go: Argument Types

At the current v0.4 stage, go: is intended for practical transpiled concurrency and supports:

  • Void
  • Int
  • Byte
  • Bool
  • Flt
  • String
  • Channel<T>
  • tuples composed of those types

Channels

Channels are typed with Channel<T>.

numbers: Channel<Int>
messages: Channel<String>

In most code, you will create them using makeChannel rather than declaring the type alone.

Creating Channels

Create a channel with:

ch: makeChannel: Int

You can also provide an explicit capacity:

ch: makeChannel: Int, 3

Capacity Notes

In the current v0.4 implementation:

  • makeChannel: T creates a channel with capacity 1
  • makeChannel: T, N creates a channel with capacity N
  • channel capacity must be at least 1

So while the model is Go-inspired, the default channel behavior is currently a single-slot channel rather than a zero-capacity unbuffered channel.

Sending and Receiving

Send a value into a channel with send:

send: ch, 42

Receive a value with recv:

x: recv: ch
print: x

Basic Example

ch: makeChannel: Int
send: ch, 42
print: recv: ch
close: ch

Worker Example

ch: makeChannel: Int

worker :: {} -> {Void}:(
    send: ch, 42
)

go: worker
print: recv: ch
close: ch

Closing Channels

Use close when you are done with a channel:

close: ch

As a general rule:

  • close channels when no more values should be sent
  • do not send to a channel after closing it
  • close channels deliberately rather than implicitly relying on program exit

Buffered Channels

Channels can hold more than one value when created with a larger capacity.

ch: makeChannel: Int, 3
send: ch, 1
send: ch, 2
send: ch, 3

print: recv: ch
print: recv: ch
print: recv: ch

close: ch

This is useful for simple producer/consumer patterns and small pipelines.

Non-Blocking Receive

Use tryRecv when you want to check a channel without blocking.

res: tryRecv: ch

The result includes:

  • res.ok as a Bool
  • res.value as the received value when available

Example:

ch: makeChannel: Int
send: ch, 42

res: tryRecv: ch

res.ok ? (
    print: res.value
) | (
    print: "empty"
)

close: ch

Timeout Receive

Use timeoutRecv to wait for a limited amount of time.

res: timeoutRecv: ch, 20

Timeout values are in milliseconds.

The result includes:

  • res.ok
  • res.value

Example:

ch: makeChannel: Int
res: timeoutRecv: ch, 20

res.ok ? (
    print: res.value
) | (
    print: "timeout"
)

close: ch

Non-Blocking Send

Use trySend when you want to attempt a send without blocking.

ok: trySend: ch, 99

This returns a Bool indicating whether the value was successfully sent.

Example:

ch: makeChannel: Int, 1

a: trySend: ch, 1
b: trySend: ch, 2

print: a
print: b

close: ch

Select-Style Receive

Hachi includes select-style receive helpers for multiple channels of the same inner type.

trySelectRecv

Checks several channels without blocking.

res: trySelectRecv: a, b

The result includes:

  • res.ok
  • res.index
  • res.value

res.index tells you which channel was selected.

Example:

a: makeChannel: Int
b: makeChannel: Int
send: b, 42

res: trySelectRecv: a, b

print: res.ok
print: res.index
print: res.value

close: a
close: b

selectRecv

Blocks until one of the channels can provide a value.

res: selectRecv: a, b

The result includes:

  • res.ok
  • res.index
  • res.value

Example:

a: makeChannel: Int
b: makeChannel: Int

producer :: {Channel<Int>} -> {Void}:(
    send: Ri.a, 20
)

go: producer: b

res: selectRecv: a, b

print: res.ok
print: res.index
print: res.value

close: a
close: b

timeoutSelectRecv

Waits up to a timeout for one of several channels to provide a value.

res: timeoutSelectRecv: a, b, 200

Timeout values are in milliseconds.

The result includes:

  • res.ok
  • res.index
  • res.value when available

Example:

a: makeChannel: Int
b: makeChannel: Int

producer :: {Channel<Int>} -> {Void}:(
    send: Ri.a, 42
)

go: producer: b

res: timeoutSelectRecv: a, b, 2000

print: res.ok
print: res.index
print: res.value

close: a
close: b

Select-Style Send

Hachi also includes select-style send helpers.

trySelectSend

Attempts to send to one of several channels without blocking.

res: trySelectSend: a, 10, b, 20

The result includes:

  • res.ok
  • res.index

Example:

a: makeChannel: Int, 1
b: makeChannel: Int, 1

res: trySelectSend: a, 10, b, 20

print: res.ok
print: res.index

close: a
close: b

selectSend

Blocks until one of several send operations can succeed.

res: selectSend: a, 10, b, 20

The result includes:

  • res.ok
  • res.index

Example:

a: makeChannel: Int, 1
b: makeChannel: Int, 1

consumer :: {Channel<Int>} -> {Void}:(
    x: recv: Ri.a
    print: x
)

go: consumer: b

res: selectSend: a, 10, b, 20

print: res.ok
print: res.index

close: a
close: b

timeoutSelectSend

Waits up to a timeout for one of several send operations to succeed.

res: timeoutSelectSend: a, 11, b, 22, 200

Timeout values are in milliseconds.

The result includes:

  • res.ok
  • res.index

Example:

a: makeChannel: Int
b: makeChannel: Int

send: a, 5

res: timeoutSelectSend: a, 11, b, 22, 200

print: res.ok
print: res.index

x: recv: b
print: x

close: a
close: b

A Small Pipeline Example

jobs: makeChannel: Int, 2
results: makeChannel: Int, 2

worker :: {} -> {Void}:(
    x: recv: jobs
    send: results, x * 2

    y: recv: jobs
    send: results, y * 2
)

send: jobs, 3
send: jobs, 4

go: worker

print: recv: results
print: recv: results

close: jobs
close: results

Current Notes and Caveats

  • The current concurrency support is first-phase and aimed at transpiled/native builds
  • Channels are typed and work best when all participating operations agree on the inner type
  • makeChannel: T currently defaults to capacity 1
  • timeout values are in milliseconds
  • tryRecv and timeoutRecv return a result object with .ok and .value
  • select-style receive operations return .ok, .index, and .value
  • select-style send operations return .ok and .index
  • selectRecv and selectSend are blocking operations
  • the transpiled runtime waits at exit for outstanding asynchronous work

For current Hachi code, a good practical style is:

  • use go: for clear worker/task boundaries
  • prefer channels for message passing instead of shared mutable state
  • use tryRecv and trySend when you want polling behavior
  • use timeout helpers when you need bounded waits
  • use select-style operations when coordinating multiple channels
  • close channels intentionally and consistently

As Hachi grows, concurrency support may continue to expand, but the features on this page reflect the current v0.4 behavior.