HHYHHY
LearnExamplesExtensionsSpec
GitHub
View on GitHubSource and commit history ↗Download source ZIPHosted here · Source ZIP1.7.1 · c5a4532
中文
中文
HHY

HHY LANGUAGE

HHY Language Manual

V1.7.1Flow-first system scripting language · Complete manualhhylang.dev

CONTENTS

Table of contents

  1. 01Quick StartGuide
  2. 02Language BasicsGuide
  3. 03Flow and StreamsGuide
  4. 04Files and PathsGuide
  5. 05Text, JSON, and CSVGuide
  6. 06Processes and SystemGuide
  7. 07HTTPGuide
  8. 08Parallel and WatchGuide
  9. 09Modules and ErrorsGuide
  10. 10Script ExamplesGuide
  11. 11Web RuntimeGuide
  12. 12Controlled Concurrency and AtomicityGuide
  13. 13Complete Syntax ReferenceReference
  14. 14Standard Library Function IndexReference
  15. 15CLI ReferenceReference
  16. 16Extension SystemExtensions
  17. 17Database Extension GuideExtensions
  18. 18HTML Extension and Crawler FrameworkExtensions
  19. 19Language and VM Evolution RoadmapRoadmap
  20. 20Editor Language SupportTooling
  21. 21HHY Language Status Report · 2026-09-14Language Reports

Guide · 01

Quick Start

Install HHY, run your first Flow, and learn the daily commands in five minutes.

1.11.7.1 controlled concurrency and atomicity

This release fixes resource lifetimes and delivers seven experimental APIs, disabled by default. Review state ownership, cooperative file locks, strict transactions and bounded-task guarantees before enabling them.

Read the complete guide and examples
Includes the guarantee matrix, failure handling and actual validation evidence
/en/learn/concurrency-scopes

1.2HHY 1.7.1 implementation and compatibility

1.7.1 passes local tests, four-platform CI and release validation. Language specification 1.0 remains frozen. Official sample, HTML and macOS/Linux Database 1.0.0 extensions are verified compatible. VS Code 0.2.0 and Sublime 0.1.0 retain independent versions; syntax and editor contract checks pass.

HIR, six optimization passes, integer MIR, type-feedback guards/deopt and local List scalar replacement are implemented. Explicitly enable HHY_COMPILER=ir, HHY_FEEDBACK_SPECIALIZATION=1 and HHY_SCALAR_REPLACEMENT=1; direct Bytecode remains default. Real workloads have not met default-enablement benefit gates. Scalar replacement retains original GC/quota reservations.

Optimization switches, verification and supported scope
v1.7.1 implementation
/docs/COMPILER_IR.md

1.7.1 release notes
Released capabilities and limits
/docs/RELEASE_NOTES_1.7.1.md

1.3Minute 1: one-command install (recommended)

The installer supports macOS arm64, Linux x86_64, and Linux arm64. It detects the platform, downloads the V1.7.1 archive and matching .sha256, and installs only after verification. sudo is not required by default.

sh
curl -fsSL https://hhylang.dev/install.sh | sh
export PATH="$HOME/.local/bin:$PATH"
hhy --version
The default version directory is ~/.local/share/hhy/1.7.1 and the command entry point is ~/.local/bin/hhy. The installer resolves the latest stable GitHub release by default; use HHY_VERSION to pin or roll back, and HHY_INSTALL_ROOT or HHY_BIN_DIR to override locations.

1.4macOS: install from the Homebrew tap

Apple Silicon Macs can use the Formula maintained in this repository. The explicit Git URL lets the repository act as a tap before a separate homebrew-tap repository is created.

sh
brew tap hh696-wq/hhy https://github.com/hh696-wq/hhy-vm.git
brew install hhy
hhy --version
The current Formula supports macOS arm64 only and pins the official archive and SHA-256. Use the installer or a Release archive on Linux.

1.5Option 2: download a Release

If you are not modifying the HHY Runtime, an official V1.7.1 archive is the fastest path. Choose darwin-arm64, linux-x86_64, or linux-arm64 for your OS and CPU. Archives include the executable, official sample and database extensions, required non-system runtime libraries, docs, licenses, and build metadata.

Open HHY GitHub Releases ↗
Download the latest stable archive and its matching .sha256 file or SHA256SUMS.
https://github.com/hh696-wq/hhy-vm/releases

sh
tar -xzf hhy-1.7.1-PLATFORM-ARCH.tar.gz
cd hhy-1.7.1-PLATFORM-ARCH
./bin/hhy --version
./bin/hhy run examples/07-language-basics.hhy
Keep bin/ and lib/ in their original relative positions so the portable executable can find bundled libraries. Replace PLATFORM-ARCH with darwin-arm64, linux-x86_64, or linux-arm64.

1.6Verify the download and add it to PATH

Before running a download, verify it with the matching .sha256 file or SHA256SUMS. macOS includes shasum; Linux commonly provides sha256sum.

sh
# macOS
shasum -a 256 -c hhy-1.7.1-darwin-arm64.tar.gz.sha256

# Linux
sha256sum -c hhy-1.7.1-linux-x86_64.tar.gz.sha256

# Add to PATH for this terminal (use the real absolute path)
export PATH="/absolute/path/hhy-1.7.1-PLATFORM-ARCH/bin:$PATH"
hhy --version

For permanent access, put the export PATH line in your shell profile. You may also keep invoking ./bin/hhy from the extracted directory without a system-wide install.

1.7Option 3: build from source

Build from source when developing the Runtime, validating current source, or choosing a custom installation prefix. HHY V1.7.1 supports macOS arm64, Linux arm64, and Linux x86_64 and requires a C11 compiler, make, libcurl, PCRE2, and BDWGC. The database extension additionally needs the corresponding PostgreSQL libpq or MySQL client development library.

sh
brew install curl pcre2 bdw-gc jansson openssl@3 zlib
git clone https://github.com/hh696-wq/hhy-vm.git
cd hhy-vm
make
make test
./build/hhy --version
The brew command applies only to macOS. Linux package names vary by distribution; see INSTALL.md for the full dependency matrix.

1.8Install the source build

sh
make install PREFIX="$(brew --prefix)"
hhy --version

PREFIX may be a custom absolute path. Once PREFIX/bin is on PATH, execute any .hhy file with hhy run.

1.9Your first script

hello.hhy
let language = "HHY"

["Flow", "Pipe", "System"]
    |> stream
    |> map { word -> language + ": " + word }
    |> print
sh
hhy check hello.hhy
hhy run hello.hhy

let creates a binding; the List literal stores three Strings; stream explicitly converts it into a Stream; |> injects the left value into the next call; the map closure creates one new String per item; print consumes the result. check validates lexical syntax, scope, modules, and known standard-library calls without effects.

1.10Running scripts and the development workflow

TaskCommandPurpose
Formathhy fmt script.hhyWrite canonical HHY formatting
Check formathhy fmt --check script.hhyVerify in CI without changing files
Check scripthhy check script.hhyValidate syntax, scope, and known APIs
Runhhy run script.hhyExecute the script
Pass argumentshhy run script.hhy input.csv output.jsonArguments enter read-only args
Preview planhhy run --dry-run script.hhyInspect a redacted plan without external effects

HHY source files use the .hhy suffix. View complete command help:

sh
hhy --help

Guide · 02

Language Basics

Variables, values, functions, conditions, loops, and scope.

2.1What dynamic typing means

HHY declarations omit types; each value carries its logical type at runtime. Dynamic does not mean coercive: conditions require Bool, String never automatically becomes Number, Bool, or Path, and invalid arity or operations raise structured errors. Use type(value) to inspect a type and is_type(value, name) to test it.

hhy
let nothing = null
let enabled = true
let count = 42
let ratio = 0.75
let title = "HHY"
let pattern = /ERROR|WARN/i
let names = ["Ada", "Linus"]
let user = { name: "Ada", active: true }
let indexes = 0..3
let size_limit = 10mib
let timeout_limit = 5s
let completion = 80%

print(type(user))
print(is_type(title, "String"))

2.2Scalar and unit types

TypeExampleUse
NullnullAbsence
BooltrueConditions and predicates
Int42Integer arithmetic
Float3.14Floating-point arithmetic
String"hello"UTF-8 text
Regex/ERROR/iText matching
Bytes10mibFile or memory size
Duration5sTimeouts and intervals
Percent80%Ratios
DateTimenow()Zoned time
Pathpath("logs")Filesystem paths

Precise String, number, unit, and Path edge cases belong in Reference. For ordinary scripts, remember that HHY never implicitly converts among String, Number, Bool, and Path.

Open the type and syntax reference →
Look up exact UTF-8, overflow, operator, and literal behavior.
/en/learn/syntax-reference

2.3List, Map, and Range

List indices start at zero and out-of-range access raises IndexError. Map keys are Strings and preserve insertion order; map.key equals map["key"]. A missing key normally returns null, while require distinguishes a missing key from a present key whose value is null. Range a..b includes a and excludes b without allocating a List.

hhy
let original = ["Flow", "System"]
let extended = append(original, "Pipe")
let shortened = remove_at(extended, 1)

let config = { retries: 3, label: null }
let updated = put(config, "timeout", 5s)
let selected = pick(updated, ["retries", "timeout"])

print(original)
print(shortened)
print(get(config, "missing"))
print(require(config, "label"))
print(selected)

Lists and Maps are not mutated in place. append, remove_at, put, remove_key, and pick return new collections, leaving original and config unchanged. Lists and Maps support deep equality; Functions, Streams, and system resources do not support value equality.

2.4Result, Stream, and system objects

TypeUsed for
ResultExplicit success values or Errors from one operation
StreamLazy files, lines, processes, responses, and events
ErrorFailures with category, location, and Flow stage
FunctionUser functions and closures
System objectDedicated values such as File, Process, and HttpResponse

System objects are not Maps. Map or pick ordinary fields before JSON encoding. Flow explains Stream laziness and consumption in detail.

2.5Bindings, scope, and immutability

hhy
let service = "api"
let mut retries = 0
retries = retries + 1

let creates a binding that cannot be reassigned; use let mut when reassignment is required. List and Map update functions return new collections rather than mutating originals. Names follow block lexical scope and must be declared before use.

Closures may capture outer values. Parallel workers use isolated snapshots; do not rely on a worker updating an outer mutable binding. Pass ordinary values, return results, and aggregate in the parent flow. See Parallel and Watch.

2.6Conditions, loops, and functions

hhy
fn classify(score) {
    if score >= 90 { return "excellent" }
    else if score >= 60 { return "pass" }
    else { return "retry" }
}

let mut total = 0
for score in [98, 72, 55] {
    if score < 60 { continue }
    total = total + score
}

let mut attempts = 0
while attempts < 3 {
    attempts = attempts + 1
}

print(classify(98))
print(total)

HHY supports if / else if / else, for item in iterable, while, break, and continue. for iterates Lists, Map entries, Ranges, or Streams; iterating a Stream consumes it. Functions use positional arguments checked at call time and return null without an explicit return.

hhy
fn summarize(items) {
    let mut total = 0

    for item in items {
        if item.enabled {
            total = total + item.score
        }
    }

    return total
}

let users = [
    { name: "Ada", enabled: true, score: 98 },
    { name: "Linus", enabled: false, score: 86 }
]

summarize(users) |> print

In the current 1.7.1 runtime, use the trailing Flow closure form { item -> expression }; a multi-statement closure must name its parameter and use return. A one-argument closure in an unambiguous Flow context may use { it * 2 }. V1.7.1 has no overloading, generics, or default arguments.

Guide · 03

Flow and Streams

Understand pipe injection, lazy streams, and single-consumption semantics.

3.1How Pipe passes values

Pipe is a composition rule for ordinary calls: x |> f means f(x), x |> f(a) means f(x, a), and x |> obj.f(a) means obj.f(x, a). It does not turn scalars into Streams, flatten nested Streams, access it fields, ignore errors, stringify values, or invoke a shell.

hhy
[1, 2, 3, 4, 5]
    |> stream
    |> map { number -> number * 2 }
    |> where { number -> number > 5 }
    |> take(2)
    |> print

3.2Stream lifecycle

A Stream is a lazy, pull-based, single-consumption sequence. Building a pipeline only composes operators; upstream produces items when a terminal starts pulling. Its lifecycle is open → next* → close, and normal completion, early take, errors, and cancellation all close resources upstream.

StageWhat happens
CreateA Source returns a Stream without reading data
ComposeOperators such as map and where form a Pipeline
ConsumeA terminal such as print, collect, or save starts pulling
CloseCompletion, early stop, Error, or cancellation releases upstream resources
A Stream is consumed once. Do not save one Stream and feed two Pipelines; recreate the Source, or explicitly collect finite input.

3.3Item, filter, and observation operators

hhy
[5, 2, 5, 1, 3]
    |> stream
    |> skip(1)
    |> take(4)
    |> inspect { number -> print("seen", number) }
    |> where { number -> number >= 3 }
    |> map { number -> number * 10 }
    |> distinct
    |> collect
    |> print

map

map(Stream<T>, Function(T -> U)) -> Stream<U>

Lazily transform each item one-to-one without automatic flattening.

where

where(Stream<T>, Function(T -> Bool)) -> Stream<T>

Lazily retain items whose predicate returns Bool true.

take

take(Stream<T>, Int) -> Stream<T>

Lazily retain the first n items and close upstream early.

skip

skip(Stream<T>, Int) -> Stream<T>

Lazily discard the first n items and pass the remainder.

inspect

inspect(Stream<T>, Function(T -> Value)) -> Stream<T>

Run an observation closure for each item and pass the item unchanged.

distinct

distinct(Stream<Hashable>) -> Stream<Hashable>

Lazily remove duplicate hashable scalars while retaining a seen set.

3.4map versus flat_map

map sends exactly the closure result downstream. Returning a Stream therefore creates Stream<Stream<T>>. flat_map requires a Stream result and concatenates each child stream into one Stream.

hhy
let batches = [[1, 2], [3, 4]]

batches
    |> stream
    |> flat_map { batch -> batch |> stream }
    |> print

3.5What barriers and terminals actually do

Item operators retain only the current item. A barrier must inspect or retain substantial input before producing a correct result: sort_by stores all input before sorting, group_by stores every group's values, collect builds a List, and terminals such as reduce/count/sum read to completion before returning a scalar. All obey memory, collection-size, and runtime limits.

hhy
let ordered = [5, 1, 3, 2, 4]
    |> stream
    |> sort_by({ order: "asc" }) { number -> number }
    |> collect

let grouped = [
    { team: "core", name: "Ada" },
    { team: "web", name: "Linus" },
    { team: "core", name: "Grace" }
]
    |> stream
    |> group_by { person -> person.team }
    |> collect

print(ordered)
print(grouped)

sort_by

sort_by(Stream<T>, Map, Function(T -> Comparable)) -> Stream<T>

Materialize finite input and stably sort by closure key and asc/desc option.

group_by

group_by(Stream<T>, Function(T -> Hashable)) -> Stream<Group<T>>

Materialize finite input into Groups containing key and values.

collect

collect(Stream<T>) -> List<T>

Consume a finite Stream and materialize it as a List.

reduce

reduce(Stream<T>, U, Function(State<T,U> -> U)) -> U

Fold a Stream from initial; the closure receives state with acc/item/index.

count

count(Stream<T>) -> Int

Consume a Stream and return its item count.

sum

sum(Stream<Number>) -> Number

Consume and sum a numeric Stream, respecting Int overflow rules.

min

min(Stream<Number>) -> Number | Null

Consume a numeric Stream and return its minimum or null for empty input.

max

max(Stream<Number>) -> Number | Null

Consume a numeric Stream and return its maximum or null for empty input.

first

first(Stream<T>) -> T | Null

Return the first item or null and close upstream early.

last

last(Stream<T>) -> T | Null

Consume a Stream and return its last item or null.

any

any(Stream<T>, Function(T -> Bool)) -> Bool

Return true on the first matching item and short-circuit upstream.

all

all(Stream<T>, Function(T -> Bool)) -> Bool

Return true only if every item matches; short-circuit on the first false.

Do not feed watch, every, or otherwise unbounded input directly into sort_by, group_by, or collect. Apply take, a time window, or another business bound first, or the Runtime raises PlanError.

3.6Effects, errors, and parallelism

Only a terminal or Action consumes a Pipeline. print, for_each, save_*, run, and send perform output, filesystem, process, or network work. Ordinary Errors terminate the Pipeline; use attempt for per-item Results and on_error to replace a failed upstream Stream.

parallel(n) uses isolated workers and preserves output order. Parallel and Watch covers concurrency limits, Sendable values, and cancellation.

Guide · 04

Files and Paths

Walk directories, read text, and write results safely.

4.11.7.1 controlled concurrency and atomicity

This release fixes resource lifetimes and delivers seven experimental APIs, disabled by default. Review state ownership, cooperative file locks, strict transactions and bounded-task guarantees before enabling them.

Read the complete guide and examples
Includes the guarantee matrix, failure handling and actual validation evidence
/en/learn/concurrency-scopes

4.2Path is not String

Every filesystem API requires Path. path(text) normalizes lexically: it collapses repeated separators and ., and resolves removable .. segments without accessing the filesystem or resolving symlinks. A relative Path is always based on the process startup directory, not the importing file.

hhy
let source = path("./src/../src/main.c")
let target = path_join(source.parent, "runtime.c")

print(source)
print(source.name)
print(source.extension)
print(source.parent)
print(target)

name, extension, and parent are read-only Path fields—not path_name(), path_extension(), or path_parent() functions. extension includes the leading dot and is empty when absent; path_join(base, child) returns a new combined Path.

4.3files: traversal, globs, and metadata

hhy
path("./logs")
    |> files("**/*.log")
    |> where { file -> file.size > 1mib }
    |> flat_map { file -> read_lines(file.path) }
    |> where { line -> contains(line, "ERROR") }
    |> save_lines(path("errors.txt"))

files(root, pattern, options?) returns a lazy Stream<File | Directory> and excludes the traversal root itself. Patterns support *, ?, and **. Directory symlinks are not followed by default; { follow_symlinks: true } opts in with cycle detection.

FieldMeaning
pathFull Path
nameFile or directory name
extensionExtension including its leading dot
sizeSize as Bytes
createdCreation time, or null when unavailable
modifiedModification time
is_file / is_dir / is_symlinkObject-kind flags

File and Directory are system objects, not Maps. Map required fields into an ordinary Map before JSON encoding.

4.4Reading text and binary data

read_text

read_text(Path) -> String

Read an entire UTF-8 file as String.

read_lines

read_lines(Path) -> Stream<String>

Lazily read UTF-8 lines with terminators removed.

read_bytes

read_bytes(Path) -> BytesBuffer

Read an entire binary file as BytesBuffer.

Text APIs validate UTF-8. Use read_bytes and write_bytes for images, archives, and arbitrary binary data rather than storing it in String.

4.5Writing, appending, and atomic saves

hhy
let input = path("notes.txt")
let backup = path("backup-notes.txt")

write_text(input, "first line\n", { overwrite: true })
append_text(input, "second line\n")
copy(input, backup, { overwrite: false })

read_lines(backup)
    |> map { line -> upper(line) }
    |> save_lines(path("backup/upper.txt"), { create_parents: true })

write_text

write_text(Path, String, Map?) -> Path

Atomically replace with String, supporting overwrite/create_parents.

append_text

append_text(Path, String) -> Path

Append String to the end of a file.

write_bytes

write_bytes(Path, BytesBuffer, Map?) -> Path

Atomically replace a file with BytesBuffer.

save_text

save_text(String | Stream<String>, Path, Map?) -> Path

Atomically save a String or pull a text Stream directly to disk.

save_lines

save_lines(Stream<String>, Path, Map?) -> Path

Write a String Stream with LF per item and atomically replace the target.

write_text, write_bytes, save_text, and save_lines accept overwrite (default true) and create_parents (default false). They commit through a same-directory temporary file plus rename; overwrite: false uses atomic no-replace to prevent check-then-write races.

4.6Copy, move, remove, and dry-run

copy

copy(Path, Path, Map?) -> Path

Copy a file with atomic no-replace; the target parent directory must already exist.

move

move(Path, Path, Map?) -> Path

Move or rename a file while respecting overwrite options.

remove

remove(Path) -> Path

Remove an explicit Path and return it.

sh
hhy run --dry-run backup.hhy
hhy run backup.hhy

The target parent directory for copy and move must already exist; the current 1.7.1 runtime does not process their create_parents option. The example uses overwrite: false; choose a new backup filename before repeating it. Review the dry-run plan before executing a script that copies, moves, or removes files.

Filesystem reads, traversal, writes, processes, and networking may stop because of RuntimeLimits, cancellation, or host permissions. The Runtime explicitly cleans resources on completion, errors, return, exit, and cancel; it does not rely on GC finalizers.

4.7Look up the complete API

Paths and files API Reference →
Look up every signature, parameter form, and stable function anchor.
/en/learn/standard-library#fn-path

Guide · 05

Text, JSON, and CSV

Process UTF-8 text, regular expressions, and structured data.

5.1String and UTF-8

String is an immutable UTF-8 byte sequence. length counts Unicode code points, byte_length counts encoded bytes, and indexing returns a one-code-point String. Text functions return new values and never mutate the original.

hhy
let line = "  ERROR: timeout  "

line
    |> trim
    |> replace("ERROR", "WARN")
    |> lower
    |> print

trim

trim(String) -> String

Remove whitespace from both ends of a String.

split

split(String, String) -> List<String>

Split a String by delimiter text into List<String>.

join

join(List<String>, String) -> String

Join List<String> with delimiter text.

replace

replace(String, String, String) -> String

Return a new String with matching text replaced.

contains

contains(String | List, Value) -> Bool

Test whether a String contains a substring or a List contains an equal value.

starts_with

starts_with(String, String) -> Bool

Test whether a String starts with the given text.

ends_with

ends_with(String, String) -> Bool

Test whether a String ends with the given text.

lower

lower(String) -> String

Return a new String converted to Unicode lowercase.

upper

upper(String) -> String

Return a new String converted to Unicode uppercase.

5.2Regex

A Regex literal is /pattern/flags with i (case-insensitive), m (multiline), s (dot matches newline), and u. regex_match returns Bool; regex_captures returns the full match, byte positions, numbered groups, and named captures, or null when unmatched.

V1.7.1 uses PCRE2 8-bit with pattern, subject, match, depth, heap, and capture limits. Exceeding them raises ResourceLimitError.

5.3JSON type mapping and errors

hhy
read_text(path("users.json"))
    |> parse_json
    |> get("users")
    |> stream
    |> where { user -> user.active == true }
    |> collect
    |> encode_json({ pretty: true })
    |> save_text(path("active-users.json"))
JSONHHY
objectMap
arrayList
stringString
integerInt
decimalFloat
true / falseBool
nullNull

parse_json reports line and column on failure. encode_json accepts { pretty: true } for readable output. Function, Stream, and system objects require mapping to ordinary fields first.

5.4CSV is a record stream

parse_csv accepts a complete String or Stream<String> and returns Stream<Map>; encode_csv accepts Stream<Map> and returns Stream<String> records without terminators. Neither requires a whole-file buffer.

hhy
read_lines(path("employees.csv"))
    |> parse_csv({ header: true })
    |> where { row -> row.active == "true" }
    |> encode_csv({ header: true })
    |> save_lines(path("active-employees.csv"))

header controls field names; delimiter and quote must be single characters. CSV performs no schema inference, so numbers and Bools require explicit conversion. encode_csv omits terminators and composes with save_lines.

5.5Look up the complete API

Text and structured data API Reference →
Look up complete signatures for text, Regex, JSON, and CSV functions.
/en/learn/standard-library#fn-contains

Guide · 06

Processes and System

Run commands, consume output, and inspect system state.

6.1The safety boundary between run and shell

hhy
run(["git", "log", "--oneline"], { timeout: 5s })
    |> stdout_lines
    |> take(10)
    |> print

run(argv, options?) passes List<String> directly to the OS without a shell, so spaces, globs, $, redirects, and pipes are not reinterpreted. Use shell(command, options?) only when shell syntax is intentional; the Checker emits a safety hint.

OptionPurpose
cwdChild working-directory Path
envOverrides only the child environment
stdinText supplied on standard input
timeoutMaximum command duration
max_outputstdout and stderr capture limit
Prefer run when values include user input. shell interprets redirects, pipes, and expansion and should be reserved for intentional shell syntax.

6.2CommandResult

run and shell wait by default and return CommandResult. A nonzero child exit code is not automatically an HHY Error; inspect exit_code for business success.

FieldContents
exit_codeChild exit status
stdoutStandard-output String
stderrStandard-error String
durationCommand Duration

A nonzero exit_code does not automatically become an HHY Error; interpret it according to the command. stdout_lines(result) exposes captured output as a line Stream.

6.3Process snapshots and fields

processes() returns a current Stream<Process> snapshot. Process is not a Map and exposes read-only pid, name, cpu, memory, status, and command fields. Explicitly map ordinary fields before JSON encoding.

sort_by({ order: "desc" }) accepts only asc or desc (default asc). The options Map is required; pass {} for defaults. It is a stable barrier that materializes the finite snapshot.

6.4args, env, system, and stdin

ValueContents
argsList<String> excluding the script path
envRead-only environment view
systemOS, architecture, host, CPU, memory, and directories
stdin_lines()Standard-input line Stream
hhy
if length(args) != 1 {
    print_error("usage: script.hhy <input>")
    exit(3)
}

let input = path(args[0])

6.5Look up the complete API

Processes and system API Reference →
Look up complete signatures for run, shell, processes, stdin_lines, and every.
/en/learn/standard-library#fn-run

Guide · 07

HTTP

Build requests, configure timeout and retry, and process responses.

7.1Request → Policy → Send → Response

hhy
http.get("https://example.com/users")
    |> timeout(5s)
    |> retry({ count: 3, backoff: 200ms })
    |> send
    |> response_body
    |> parse_json
    |> print
example.com and api.example.com are placeholders; replace them with an endpoint that returns JSON. Video lesson 11 includes a local API fixture you can run on your computer.

http.get/post/put/delete(url, options?) only build immutable HttpRequests without network access. timeout(request, duration) and retry(request, options) return new policy-adjusted Requests; send(request) performs the network effect and returns HttpResponse. Dry-run can therefore inspect the full plan without execution.

7.2Request options and safe defaults

OptionPurpose
queryURL query parameters
headersRequest headers
bodyRequest content
proxyProxy address
follow_redirectsRedirect policy
allow_private_networksWhen false, reject private, loopback, and link-local resolved connection addresses

TLS verification is enabled by default. Sensitive Authorization and Cookie headers are redacted in plans, logs, and Errors. Response bodies obey max_http_body.

7.3Timeout, retries, and idempotency

retry({ count, backoff }) defaults to connection errors, timeouts, 429, and selected 5xx statuses. GET, PUT, and DELETE may retry by policy; POST does not retry automatically to avoid duplicate creation or charges. Timeout and Ctrl+C cancel libcurl work and release response resources.

Retries do not erase failure. Give every request a timeout, evaluate POST idempotency, and preserve method, redacted URL, attempts, and Flow stage in the final Error.

7.4HttpResponse and response bodies

send returns an in-memory HttpResponse. Use response_body for UTF-8 text and response_bytes for binary data; send_to(request, path) writes directly from curl into a sibling temporary file and atomically publishes it, returning only path and size.

hhy
http.get("https://api.example.com/status")
    |> timeout(3s)
    |> send
    |> response_body
    |> parse_json
    |> print

7.5Look up the complete API

HTTP API Reference →
Look up request builders, timeout, retry, send, and response readers.
/en/learn/standard-library#fn-http-get

Guide · 08

Parallel and Watch

Bounded parallel work, cancellation, and filesystem event streams.

8.11.7.1 controlled concurrency and atomicity

This release fixes resource lifetimes and delivers seven experimental APIs, disabled by default. Review state ownership, cooperative file locks, strict transactions and bounded-task guarantees before enabling them.

Read the complete guide and examples
Includes the guarantee matrix, failure handling and actual validation evidence
/en/learn/concurrency-scopes

8.2parallel is a bounded concurrent map

hhy
let urls = [
    "https://example.com",
    "https://example.org"
]

urls
    |> stream
    |> parallel(2) { url ->
    http.get(url)
        |> timeout(5s)
        |> send
}
    |> print
BehaviorGuarantee
ConcurrencyAt most n workers, subject to RuntimeLimits
Output orderMatches input order
BackpressureInput and result buffers are bounded
ErrorsThe first unhandled Error cancels remaining work
ReturnConcurrent map; child Streams are not flattened

If a closure returns Stream, apply flat_map after parallel. Dry-run creates no workers but still checks closure Effects in order.

8.3Sendable values and isolation

Workers receive frozen snapshots of input and captured values, never shared mutable objects. Null, Bool, numbers, String, units, Path, and ordinary Lists/Maps/system snapshots whose fields are sendable can be copied.

Do not send Streams, open File handles, request body streams, or other process-local resources to workers. The tested 1.7.0 build permits mutable-binding captures as isolated snapshots without updating the parent scope; do not rely on capture checking to reject every let mut. V1.7.1 exposes no threads, locks, or async/await.

8.4watch and FileEvent

hhy
watch(path("./src"))
    |> where { event -> event.kind == "created" or event.kind == "modified" }
    |> debounce(300ms)
    |> for_each { event ->
    print(event.path)
}

watch(path, { recursive? }) returns an infinite Stream<FileEvent>. FileEvent exposes read-only kind, path, old_path, and timestamp; kind is created, modified, removed, or renamed, and old_path exists only for renamed. Event sequences vary by platform and editor; first create a file in a test directory to verify the watcher, then check modification and rename behavior.

FieldContents
kindcreated, modified, removed, or renamed
pathTarget Path
old_pathOriginal Path for renamed; otherwise null
timestampEvent DateTime

watch is an infinite Stream. End it with Ctrl+C, timeout, or cancel. Recursive watch obeys max_open_files, and filesystems may merge rapid duplicate events.

8.5debounce and every

debounce(window) is leading-edge: the first scalar or kind + path FileEvent emits immediately; duplicates inside the window are coalesced and reset the window. Different event keys do not block each other.

every(duration) returns an infinite tick Stream. If downstream is busy, backpressure prevents overlapping the same tick. Apply take or a business window before collect/sort/group.

8.6Cancellation and cleanup

Ctrl+C, timeout, cancel(), and unhandled errors trigger one root CancellationToken. Watchers, sleep, HTTP, child processes, and workers poll it; cancellation closes queues and handles, terminates children, and propagates Stream close upstream.

Guide · 09

Modules and Errors

Organize code, propagate structured errors, and unwind resources reliably.

9.1Import forms and path resolution

hhy
import { add } from "./math.hhy"

add(20, 22) |> print
FormUse
import "./lib/report.hhy" as reportImport a local module namespace
import { parse } from "./lib/data.hhy"Named import
import { validate as check } from "./lib/data.hhy"Named import with alias
import httpImport a standard-library module

Relative paths resolve from the current source file. Standard modules use bare names; local files explicitly use ./, ../, or an absolute Path.

9.2Exports, scope, and execution

hhy
export let version = "1.0"

export fn normalize_name(name) {
    return name |> trim |> lower
}

fn internal_helper() {
    return null
}

Only exported names are visible to importers. A module owns its top-level scope, executes once on first import, and is then cached. Import cycles raise CheckError before execution. V1.7.1 supports standard and local modules plus process-extension modules installed from local packages.

9.3Error fields and categories

Every failure uses Error rather than null or printed text. Error exposes kind, code, message, source, stage, cause, stack, and context. Sensitive headers, credentials, and full file contents are excluded from context by default.

Built-in categories include SyntaxError, CheckError, TypeError, ValueError, IndexError, KeyError, EncodingError, IoError, ProcessError, HttpError, HttpStatusError, TimeoutError, CancelledError, ResourceLimitError, and PlanError.

9.4try/catch and rethrowing

hhy
try {
    read_text(path("config.json"))
        |> parse_json
        |> print
} catch err {
    print_error(err)
    exit(1)
}

catch receives the first Error propagated from try. Execution continues after a normally completed catch; use throw(err) to preserve the error chain when it cannot be handled. An unhandled Error exits nonzero.

9.5Flow errors and per-item Result

hhy
path("./configs")
    |> files("**/*.json")
    |> map { file -> attempt { read_text(file.path) } }
    |> where { result -> result.ok }
    |> map { result -> result.value }
    |> print

An unhandled Error in a Stream terminates the Pipeline. attempt converts one operation to Result for batches that explicitly retain successes and failures. on_error replaces an entirely failed upstream Stream and never silently skips an item.

9.6Resource cleanup guarantees

Error, return, exit, timeout, Ctrl+C, and cancel share one unwind path. Stream close is idempotent; failed atomic saves delete temporary files and preserve the old file; child processes, HTTP responses, watchers, and workers respond to cancellation.

Guide · 10

Script Examples

Use a single script for a focused task: filtering logs, calling APIs, processing files, or transforming data.

10.1Hello HHY and Flow

The smallest runnable example: turn a List into a Stream, map and filter values, then print the result. Corresponds to examples/00-hello.hhy.

00-hello.hhy
let language = "HHY"

["Flow", "Pipe", "System"]
    |> stream
    |> map { word -> language + ": " + word }
    |> print
$ hhy run examples/00-hello.hhy
HHY: Flow
HHY: Pipe
HHY: System

✓ exit 0 · Flow pipeline completed

10.2Extract log alerts concurrently

Recursively scan large log files with four workers, extract ERROR/WARN lines, and retain each source path. Useful for incident response and scheduled log jobs.

log-errors.hhy
if length(args) != 2 {
    print_error("usage: hhy run log-errors.hhy <log-dir> <output-file>")
    exit(3)
}

let log_dir = path(args[0])
let output_file = path(args[1])

log_dir
    |> files("**/*.log")
    |> where { file -> file.size > 1mib }
    |> parallel(4) { file ->
    read_lines(file.path)
        |> where { line -> regex_match(line, /ERROR|WARN/) }
        |> map { line -> encode_json(file.path) + ": " + line }
        |> collect
}
    |> flat_map { lines -> lines |> stream }
    |> save_lines(output_file)
    |> on_error { err ->
    print_error(err)
    throw(err)
}
sh
hhy run log-errors.hhy ./logs ./output/errors.txt
$ hhy run log-errors.hhy ./logs ./output/errors.txt && head -3 ./output/errors.txt
logs/api.log: 2026-08-25T09:18:42Z ERROR database timeout after 3000ms
logs/worker.log: 2026-08-25T09:18:44Z WARN retrying job #1842
logs/api.log: 2026-08-25T09:18:47Z ERROR upstream returned 502

✓ exit 0 · 3 alerts written to output/errors.txt

10.3Sync active users from an API

Fetch users with timeout and retry, parse JSON, select fields, and atomically save only active users. Corresponds to examples/02-active-users.hhy.

active-users.hhy
if length(args) != 2 {
    print_error("usage: hhy run active-users.hhy <url> <output-file>")
    exit(3)
}

http.get(args[0])
    |> timeout(5s)
    |> retry({ count: 3, backoff: 200ms })
    |> send
    |> response_body
    |> parse_json
    |> get("users")
    |> stream
    |> where { user -> user.active == true }
    |> map { user ->
    { id: user.id, name: user.name, email: user.email }
}
    |> collect
    |> encode_json({ pretty: true })
    |> save_text(path(args[1]))
    |> on_error { err ->
    print_error(err)
    throw(err)
}
sh
hhy run active-users.hhy https://api.example.com/users active-users.json
$ hhy run active-users.hhy http://127.0.0.1:9000/users active-users.json && cat active-users.json
[
  { "id": 101, "name": "Ada", "email": "ada@example.com" },
  { "id": 108, "name": "Linus", "email": "linus@example.com" }
]

✓ exit 0 · 2 active users written to active-users.json

10.4Monitor process CPU and memory

Sample processes every five seconds, keep CPU-heavy or memory-heavy entries, and print the top ten by memory. Corresponds to examples/03-process-monitor.hhy.

03-process-monitor.hhy
every(5s)
    |> for_each { tick ->
    processes
        |> where { process ->
        process.cpu > 70% or process.memory > 1gib
    }
        |> sort_by({ order: "desc" }) { process -> process.memory }
        |> take(10)
        |> map { process ->
        {
            pid: process.pid,
            name: process.name,
            cpu: process.cpu,
            memory: process.memory
        }
    }
        |> print
}
$ hhy run examples/03-process-monitor.hhy
[{ pid: 8421, name: "node", cpu: 82.4%, memory: 1.42 GiB },
 { pid: 9107, name: "hhy",  cpu: 74.1%, memory: 86.3 MiB }]

next sample in 5s… · Ctrl+C exits safely

10.5Check service health in batches

Probe multiple services concurrently with consistent timeouts and retries. A failed endpoint is recorded without terminating the whole batch.

health-check.hhy
let services = [
    { name: "users", url: "https://api.example.com/users/health" },
    { name: "orders", url: "https://api.example.com/orders/health" },
    { name: "billing", url: "https://api.example.com/billing/health" }
]

services
    |> stream
    |> parallel(3) { service ->
    let response = attempt {
        http.get(service.url)
            |> timeout(3s)
            |> retry({ count: 2, backoff: 100ms })
            |> send
            |> response_body
            |> parse_json
    }

    let mut status = "unreachable"
    let mut error_message = null

    if response.ok {
        status = response.value.status
    } else {
        error_message = response.error.message
    }

    return {
        name: service.name,
        ok: response.ok,
        status: status,
        error: error_message
    }
}
    |> collect
    |> encode_json({ pretty: true })
    |> print
$ hhy run health-check.hhy
[
  { "name": "users",   "ok": true,  "status": "healthy", "error": null },
  { "name": "orders",  "ok": true,  "status": "healthy", "error": null },
  { "name": "billing", "ok": false, "status": "unreachable", "error": "request timed out" }
]

✓ exit 0 · all 3 checks completed despite one endpoint failure

10.6Release quality gate

Run tests, lint, and production builds in parallel, save a machine-readable report, and block a release with a stable exit code when any check fails.

release-gate.hhy
let checks = [
    { name: "unit-tests", command: ["make", "test"] },
    { name: "lint", command: ["npm", "run", "lint"] },
    { name: "production-build", command: ["npm", "run", "build"] }
]

let report = checks
    |> stream
    |> parallel(3) { check ->
    let result = run(check.command, { timeout: 10min, max_output: 8mib })
    return {
        name: check.name,
        passed: result.exit_code == 0,
        exit_code: result.exit_code,
        output: result.stdout
    }
}
    |> collect

report |> encode_json({ pretty: true }) |> save_text(path("release-gate.json"))

if report |> stream |> any { check -> check.passed == false } {
    print_error("release blocked: one or more checks failed")
    exit(1)
}

print("release gate passed")
$ hhy run release-gate.hhy
unit-tests       PASS  4.28s
lint             PASS  1.14s
production-build PASS  6.72s
release-gate.json written

✓ exit 0 · release gate passed

10.7Source secret audit

Scan configuration and source files concurrently for suspected API keys, passwords, and private keys, then produce a security review report.

secret-audit.hhy
if length(args) != 2 {
    print_error("usage: hhy run secret-audit.hhy <source-dir> <report-file>")
    exit(3)
}

path(args[0])
    |> files("**/*")
    |> where { file ->
    file.extension == ".env" or
    file.extension == ".yml" or
    file.extension == ".json" or
    file.extension == ".ts"
}
    |> parallel(4) { file ->
    read_lines(file.path)
        |> where { line ->
        regex_match(line, /API_KEY|SECRET|PASSWORD|BEGIN PRIVATE KEY/)
    }
        |> map { line -> encode_json(file.path) + ": " + line }
        |> collect
}
    |> flat_map { matches -> matches |> stream }
    |> save_lines(path(args[1]))
$ hhy run secret-audit.hhy ./services secret-findings.txt
services/billing/.env: PAYMENT_API_KEY=***
services/auth/config.yml: PASSWORD: ***

✓ exit 0 · 2 suspected secrets require review
The example output is redacted. Restrict report access and avoid printing raw secrets in production pipelines.

10.8Order and payment reconciliation

Merge order and payment CSV files into one flow, group records by order_id, and report missing records or mismatched amounts.

reconcile.hhy
if length(args) != 3 {
    print_error("usage: hhy run reconcile.hhy <orders.csv> <payments.csv> <report.json>")
    exit(3)
}

[path(args[0]), path(args[1])]
    |> stream
    |> flat_map { input ->
    read_lines(input) |> parse_csv({ header: true })
}
    |> group_by { record -> record.order_id }
    |> where { group ->
    (group.values |> stream |> count) != 2 or
    (group.values |> stream |> map { record -> record.amount } |> distinct |> count) != 1
}
    |> map { group ->
    { order_id: group.key, records: group.values, issue: "missing_or_amount_mismatch" }
}
    |> collect
    |> encode_json({ pretty: true })
    |> save_text(path(args[2]))
$ hhy run reconcile.hhy orders.csv payments.csv exceptions.json
orders: 12,480 · payments: 12,472
matched: 12,461
exceptions: 19 → exceptions.json

✓ exit 0 · reconciliation report written atomically

10.9Multi-tenant usage snapshot

Fetch tenant usage with bounded concurrency, consistent retries, and failure isolation. Useful for billing, capacity analysis, and customer success reports.

tenant-snapshot.hhy
let tenants = [
    { id: "acme", url: "https://api.example.com/acme/usage" },
    { id: "nova", url: "https://api.example.com/nova/usage" },
    { id: "orbit", url: "https://api.example.com/orbit/usage" }
]

tenants
    |> stream
    |> parallel(3) { tenant ->
    let result = attempt {
        http.get(tenant.url)
            |> timeout(5s)
            |> retry({ count: 3, backoff: 200ms })
            |> send
            |> response_body
            |> parse_json
    }

    if result.ok {
        return { tenant: tenant.id, ok: true, usage: result.value, error: null }
    }

    return { tenant: tenant.id, ok: false, usage: null, error: result.error.message }
}
    |> collect
    |> encode_json({ pretty: true })
    |> save_text(path("tenant-usage-snapshot.json"))
$ hhy run tenant-snapshot.hhy
acme  ✓ requests=184203 storage_gb=82.4
nova  ✓ requests=99102  storage_gb=41.8
orbit ✗ request timed out

✓ exit 0 · snapshot contains both data and failure reasons

10.10Oversized asset governance

Find large image and video assets, sort them by size, and produce a JSON inventory for compression or storage migration work.

asset-audit.hhy
if length(args) != 2 {
    print_error("usage: hhy run asset-audit.hhy <asset-dir> <report.json>")
    exit(3)
}

path(args[0])
    |> files("**/*")
    |> where { file ->
    file.is_file and
    (file.extension == ".png" or file.extension == ".jpg" or file.extension == ".mp4")
}
    |> where { file -> file.size > 5mib }
    |> sort_by({ order: "desc" }) { file -> file.size }
    |> map { file ->
    { path: file.path, bytes: file.size, extension: file.extension }
}
    |> collect
    |> encode_json({ pretty: true })
    |> save_text(path(args[1]))
$ hhy run asset-audit.hhy ./public asset-report.json
scanned 1,842 assets
large assets: 27
largest: public/video/launch.mp4 · 184.2 MiB

✓ exit 0 · asset-report.json generated

10.11Watch sources and rebuild

Watch C sources, debounce rapid saves, and run make. Build failures are reported while the watcher stays alive.

watch-build.hhy
if length(args) != 1 {
    print_error("usage: hhy run watch-build.hhy <source-dir>")
    exit(3)
}

let source_dir = path(args[0])

watch(source_dir, { recursive: true })
    |> where { event ->
    event.kind != "removed" and
    (event.path.extension == ".c" or event.path.extension == ".h")
}
    |> debounce(300ms)
    |> for_each { event ->
    print("changed:", event.path)

    let result = run(["make"], { timeout: 2min, cwd: system.cwd })

    if result.exit_code != 0 {
        print_error(result.stderr)
    } else {
        print(result.stdout)
    }
}
sh
hhy run watch-build.hhy ./src
$ hhy run watch-build.hhy ./src
watching ./src recursively…
changed: src/runtime/flow.c
cc -std=c11 -O2 -c src/runtime/flow.c
cc build/*.o -lcurl -lpcre2-8 -lgc -o build/hhy
Build complete: build/hhy

✓ watcher remains active · waiting for the next change

10.12Build a department report from CSV

Read employee CSV records, keep active employees, aggregate headcount and salary by department, and atomically save formatted JSON.

csv-report.hhy
if length(args) != 2 {
    print_error("usage: hhy run csv-report.hhy <input.csv> <output.json>")
    exit(3)
}

read_lines(path(args[0]))
    |> parse_csv({ header: true })
    |> where { employee -> employee.active == "true" }
    |> group_by { employee -> employee.department }
    |> map { group ->
    {
        department: group.key,
        employees: group.values |> stream |> count,
        total_salary: group.values
            |> stream
            |> map { employee -> employee.salary |> to_float }
            |> sum
    }
}
    |> collect
    |> encode_json({ pretty: true })
    |> save_text(path(args[1]))
sh
hhy run csv-report.hhy employees.csv department-report.json
$ hhy run csv-report.hhy employees.csv department-report.json && cat department-report.json
[
  { "department": "Engineering", "employees": 12, "total_salary": 2160000 },
  { "department": "Product", "employees": 5, "total_salary": 810000 }
]

✓ exit 0 · department-report.json written atomically

10.13Back up large files with dry-run

Create the backup directory first (for example, mkdir -p ./backup). Find files over 100 MiB and copy them into that directory. Inspect the plan with dry-run before performing real writes.

backup-large.hhy
if length(args) != 2 {
    print_error("usage: hhy run backup-large.hhy <source-dir> <backup-dir>")
    exit(3)
}

let source_dir = path(args[0])
let backup_dir = path(args[1])

source_dir
    |> files("**/*")
    |> where { file -> file.is_file and file.size > 100mib }
    |> for_each { file ->
    let target = path_join(backup_dir, file.name)
    print("copy", file.path, "->", target)
    copy(file.path, target, { overwrite: false })
}
    |> on_error { err ->
    print_error(err)
    throw(err)
}
sh
hhy run --dry-run backup-large.hhy ./downloads ./backup
hhy run backup-large.hhy ./downloads ./backup
$ hhy run --dry-run backup-large.hhy ./downloads ./backup
copy downloads/archive.tar -> backup/archive.tar
copy downloads/database.dump -> backup/database.dump
[dry-run] copy downloads/archive.tar → backup/archive.tar
[dry-run] copy downloads/database.dump → backup/database.dump

✓ exit 0 · plan generated without writing files
The recipe refuses to overwrite files and creates parent directories, but you should still inspect the dry-run plan first.

10.14Language basics in one task

A small aggregation task combining variables, Lists, Maps, functions, conditions, loops, scopes, and error handling. Corresponds to examples/07-language-basics.hhy.

07-language-basics.hhy
fn summarize(items) {
    let mut total = 0

    for item in items {
        if item.enabled {
            total = total + item.score
        }
    }

    return total
}

let users = [
    { name: "Ada", enabled: true, score: 98 },
    { name: "Linus", enabled: false, score: 86 }
]

summarize(users) |> print
$ hhy run examples/07-language-basics.hhy
{
  "count": 2,
  "total": 40,
  "average": 20
}

✓ exit 0 · summary generated

Guide · 11

Web Runtime

Build persistent JSON APIs, streaming services, and multi-worker applications with HHY.

11.1Start with one API

v1.4.3 contains the complete v1.4.0–v1.4.3 Web release train. Each worker loads the application once, handlers are reusable, and one failed request returns 500 without terminating later requests.

hhy
import web

fn hello(request) {
    web.json({ ok: true, name: request.query_params.name })
}

web.app()
    |> web.request_id
    |> web.health
    |> web.metrics
    |> web.gzip
    |> web.get("/hello", hello)
    |> web.listen({ host: "127.0.0.1", port: 8080, workers: 4 })
sh
hhy serve app.hhy
hhy serve --dev app.hhy

11.2v1.4.3 capability boundary

LayerCapabilities
RuntimeOpaque C embedding, JSON boundary, AST/Bytecode, 100,000-call repetition test
HTTPHTTP/1.1, Router, Query/Header/Cookie, hard body limits, stable 4xx/5xx
ApplicationMiddleware, ETag static files, upload, CORS, signed cookies, trusted proxy, hot reload
ProductionStream, SSE, Range, prefork workers, health checks, structured logs, Prometheus metrics
Deploy behind Caddy, Nginx, or a managed load balancer for TLS/HTTP/2; WebSocket is outside v1.4. Enable web.trust_proxy only when every direct client is a trusted proxy.

11.3Use the HHY Web framework

HHY Web is a separate lightweight framework built on the v1.4.3 Web Runtime. It combines a Flask-inspired approachable API with Go-like explicit deployment, adding an application factory, five HTTP verbs, middleware, blueprints, stable Problem JSON, bearer authentication, uploads, Stream, and SSE helpers without duplicating the Router, worker model, or network stack.

View HHY Web on GitHub ↗
The public repository includes English and Chinese guides, Hello World, a complete JSON API, CLI, unit tests, and a real HTTP smoke test.
https://github.com/hh696-wq/hhy-web

sh
git clone https://github.com/hh696-wq/hhy-web.git
cd hhy-web
./bin/hhy-web run examples/hello/app.hhy --port 8000 --dev
curl http://127.0.0.1:8000/
app.hhy
import "./lib/hhyweb.hhy" as hhyweb

fn hello(request) {
    return hhyweb.json({
        message: "Hello, HHY Web!",
        request_id: request.id
    })
}

hhyweb.minimal()
    |> hhyweb.get("/", hello)
    |> hhyweb.serve({ host: "127.0.0.1", port: 8000, workers: 1 })
HHY Web is currently at 0.1.0 and requires HHY Language v1.4.3 or newer. Using import web directly remains the stable, supported low-level path.

Guide · 12

Controlled Concurrency and Atomicity

Seven experimental APIs in 1.7.1: ownership, effects, transaction and cancellation boundaries, with runnable examples.

12.1Scope and opt-in activation

Enabled by default: long-lived Context environments remain GC roots; Stream close releases payload references; supervised children have independent quota-failure recovery and teardown; portable no-replace uses exclusive publication. Language specification stays 1.0 and Database stays 1.0.0.

The seven APIs below are experimental and disabled by default. HHY uses synchronous runtimes and process tasks. This release adds no coroutine scheduler, async/await, shared-memory STM or global atomicity. Concurrent calls to the same HhyContext from multiple threads are not guaranteed safe.
sh
HHY_CONCURRENCY_EXPERIMENTS=1 hhy run --engine bytecode atomic-state.hhy
HHY_CONCURRENCY_EXPERIMENTS=1 hhy run --engine ast atomic-state.hhy
hhy contracts --format json

The shell variable above applies to one command. In PowerShell set $env:HHY_CONCURRENCY_EXPERIMENTS="1" and remove it afterward with Remove-Item Env:HHY_CONCURRENCY_EXPERIMENTS. Dry-run records contracts and returns Null; it neither executes callbacks nor validates their actual results.

12.2Atomicity and consistency guarantees

DomainCommit and failureBoundary
AtomicStateOne owner publishes a complete candidate; precommit failure preserves old dataNo state sharing across runtimes or workers
Single fileAtomic replacement under a stable sidecar lock; durability may be unknown after publicationCooperating writers only; no multi-file transaction or network-filesystem guarantee
DatabaseServer COMMIT; distinguish rollback from unknown commitIsolation, SQL, constraints and idempotency determine consistency
task_mapOrdered return after success; failure terminates and reaps direct tasksPreviously performed external effects are not rolled back
Ordinary variablesExisting semantics and isolated captures remainVariables do not become implicitly atomic

12.3Seven experimental APIs

atomic_state

atomic_state(data)

Create state owned by one Runtime/PID.

atomic_read

atomic_read(state)

Read an immutable snapshot.

atomic_update

atomic_update(state, callback)

Validate a pure callback candidate and publish synchronously; precommit errors preserve the old value.

atomic_close

atomic_close(state)

Close the handle; later access and repeated close fail.

atomic_file_update

atomic_file_update(path, timeout, callback)

Read and replace an existing regular file under a cooperative lock; timeout bounds lock waiting only.

transaction_strict

transaction_strict(config, options?, callback)

Bind SQL to the current tx; caught errors still prevent commit; reconcile unknown commits.

task_map

task_map(data, parallelism, result_limit, callback)

Execute isolated processes, return an ordered List, and bound direct children and accepted results.

12.4State updates and handle ownership

atomic-state.hhy
let state = atomic_state({left: 0, right: 100})
fn transfer(old) { {left: old.left + 1, right: old.right - 1} }
atomic_update(state, transfer)
print(atomic_read(state))
atomic_close(state)

After this example, left is 1 and right is 99. atomic_update returns the new value and atomic_read reads an immutable snapshot. Data is limited to Null, Bool, Int, finite Float, String and recursive List/Map values with a depth limit of 128. Functions, Streams and handles cannot enter state.

AtomicState is Runtime/PID-bound and cannot be JSON-encoded, sent to tasks or captured across workers. Every access validates owner, PID, closed and busy status; repeated close also fails. Precommit errors, cancellation and quota failure preserve old data without automatic retry.

12.5Effect contracts and commit state machine

strict_capability is pure, bound_transaction or external, with the Runtime contracts as the closed allowlist. Strict callbacks reject I/O, tasks, Streams, clocks, randomness, imports, assignments (including local mut) and nested strict scopes. Database callbacks additionally allow query/execute bound to the current tx. Third-party extensions cannot bypass restrictions by declaring themselves pure.

text
ACTIVE -> COMMITTING -> COMMITTED
   |           |----> ABORTED
   |           `----> UNKNOWN
   `----> ABORTED
Catching a callback error leaves the scope rollback-only and cannot restore commit eligibility. The checker rejects known violations in direct closures; the Runtime validates indirect calls. AST and Bytecode share the commit core, while HIR/MIR retain effect, exception and cancellation barriers. This is not a whole-program purity proof.

12.6Cooperative single-file updates

atomic-file.hhy
fn increment(text) { encode_json(to_int(text) + 1) }
atomic_file_update(path(args[0]), 3s, increment) |> print
sh
printf '0' > /tmp/hhy-counter.txt
HHY_CONCURRENCY_EXPERIMENTS=1 hhy run atomic-file.hhy /tmp/hhy-counter.txt

The file must already exist and the callback maps String to String. Lock-wait Duration must be positive and at most 300s; it is not a callback deadline. All writers must use the same path and <target>.hhy-lock protocol. Symlinks, multiple hard links and .hhy-lock target names are rejected. Keep the lock file while writers are active. Inode, ACL and extended-attribute preservation are not guaranteed.

HHY_PUBLISHED_DURABILITY_UNKNOWN means replacement was published but directory durability is unknown. HHY_PUBLISHED_CLEANUP_INCOMPLETE means no-replace published but source cleanup failed. Do not blindly retry as if nothing was written. Re-read business state after process failure or postcommit resource exhaustion as well.

12.7Restricted database transactions and durable idempotency

strict-database.hhy
import database
let cfg = read_text(path(args[0])) |> parse_json
let n = cfg |> transaction_strict { tx ->
    database.execute(tx, "UPDATE counters SET n = n + 1 WHERE id = 1", [])
    return database.query(tx, "SELECT n FROM counters WHERE id = 1", []).rows[0].n
}
print(n)

Install Database 1.0.0 first, create a counters table with an existing id=1 row and integer n, then pass the database configuration JSON path as args[0]. Follow the database extension configuration format and keep credentials in local configuration. Options support the existing isolation/read_only settings.

Callbacks cannot use another tx, a pool, streaming handles, manual commit/savepoint, or return tx even nested in a result. MySQL implicit-commit statements are rejected. Effects inside stored procedures and triggers remain governed by the server; the Runtime cannot prove arbitrary SQL free of external effects.

DB_COMMIT_UNKNOWN does not mean rolled back. Store a unique business request key and the business update in the same transaction for durable idempotency; query that key and reconcile after an unknown commit. There is no automatic retry middleware or combined rollback across database, files and HTTP.

Database installation and configuration
Database remains independently versioned at 1.0.0
/en/learn/database-extension

12.8Bounded tasks, cancellation and existing parallel

bounded-task.hhy
let results = [1, 2, 3] |> task_map(2, 1mib) { n -> n * 2 }
print(results)

The example returns [2, 4, 6] in input order. task_map eagerly maps a List to a List using isolated subprocesses; parallel keeps its existing Stream behavior. Parallelism is a positive integer bounded by Runtime limits. The 1b–256mib result budget limits cumulative accepted serialized results.

Each child also has RLIMIT_FSIZE; pending result files can total parallelism × result limit, while input/output Lists remain subject to memory limits. Later-task failures are detected without waiting for the first slow task. Shutdown sends TERM to all direct children, shares an approximately 500ms grace period, then KILLs and reaps as needed. This is not a real-time guarantee, does not guarantee cleanup of detached descendants, and does not roll back external effects.

12.9Validation evidence and remaining limits

  • 69 local engine/configuration cases; 10,000 commits per engine; GC, owner, quota and effect-denial tests.
  • 80 competing file increments; native/portable no-replace races; MySQL/PostgreSQL tests with 32 requests, eight idempotency keys and dropped commit-acknowledgement reconciliation.
  • A 120-second soak and 100,000-request Web acceptance passed; these are not a 24-hour soak, real power-loss test or general shared-memory linearizability proof.
  • Four-platform CI and release validation passed; Windows artifacts use MSYS2 and exclude Database.
  • HHY_BYTECODE_BINDING_CACHE=1 and HHY_STREAM_SOURCE_FUSION=1 remain separate, disabled-by-default experiments. Evidence does not justify default enablement or general speedup claims.

1.7.1 four-platform CI
f0fec9d · 2026-09-14
https://github.com/hh696-wq/hhy-vm/actions/runs/34805271378

Real-database acceptance
MySQL / PostgreSQL
https://github.com/hh696-wq/hhy-vm/actions/runs/34805271372

1.7.1 release notes and artifacts
Published; remaining 1.7.x candidates retain their individual acceptance gates
https://github.com/hh696-wq/hhy-vm/releases/tag/v1.7.1

12.10Offline Markdown guide

Download the English guide
Markdown version of this guide
/docs/CONCURRENCY_GUIDE.en.md

Reference · 13

Complete Syntax Reference

V1.7.1 lexical rules, literals, operators, statements, closures, and module syntax.

13.1Source files and lexical rules

ItemRule
File.hhy, UTF-8, LF or CRLF
IdentifierCase-sensitive ASCII letters, digits, and underscores; cannot start with a digit
Statement endNewline or optional semicolon
ContinuationOpen delimiters or leading/trailing |>
Comment# line comment; first line may be a shebang
/Regex at expression start, division after a left operand

13.2Literals and native units

hhy
let nothing = null
let flags = [true, false]
let numbers = [42, -10, 0xff, 0b1010, 1.5, 1e6]
let name = "HHY"
let strings = ["hello", "Hello, " + name]
let pattern = /ERROR|WARN/i
let list = [1, 2, 3]
let record = { name: "Tom", age: 20 }
let interval = 1..10
let units = [10mib, 5s, 80%]

Ranges include the start and exclude the end. Bytes support b/kb/mb/gb/kib/mib/gib; Duration supports ns/us/ms/s/min/h; % attached to a number creates Percent. In the current 1.7.1 runtime, {name} inside a string remains literal text. Use + to join strings, or pass multiple arguments to print for other value types. Strings support \, ", \n, \r, \t, \b, \f, and \0 escapes.

13.3Operator precedence (highest to lowest)

text
()  []  .
not  -  +
*  /  %
+  -
<  <=  >  >=
==  !=
and
or
??
|>
=

and, or, and ?? short-circuit; = may assign only to a let mut binding; |> is left-associative. Conditions require Bool—0, empty strings, and null are not implicitly false.

13.4Declarations, control flow, functions, and modules

hhy
let name = "HHY"
let mut count = 0
count = count + 1
let enabled = true
let items = ["Flow", "Pipe"]

if enabled { print("yes") } else { print("no") }
for item in items { print(item) }
while count < 3 { count = count + 1 }

fn add(a, b) { return a + b }
let doubled = [1, 2] |> stream |> map { number -> number * 2 } |> collect

try { read_text(path("config.json")) } catch err { print_error(err) }
let result = attempt { read_text(path("config.json")) }

import { add as sum_two } from "./math.hhy"
export fn public_api(value) { return value }
ConstructForm
Callname(args)
Pipex |> f(a) equals f(x, a)
Closure{ param -> expression }
Map{ key: value }
Control flowif, for, while, break, continue, return
Moduleimport, as, export

13.5Core value types

text
Null Bool Int Float String Regex BytesBuffer
List Map Range Function Error Result Stream
Bytes Duration Percent DateTime Path
File Directory FileEvent Process CommandResult
HttpRequest HttpResponse
HHY is dynamically typed but does not perform dangerous String/Number or String/Bool coercions. Int is signed 64-bit and Float is IEEE 754 double.

Reference · 14

Standard Library Function Index

Signatures and purposes for all 128 V1.7.1 core callables in the runtime Registry.

14.1Reading the signatures

This page is sourced from the V1.7.1 Runtime Callable Contract Registry and contains all 128 core callables; dynamically registered callables are documented by their extensions. T/U are generic placeholders, ? marks an optional argument or nullable result, and Map? is an optional options Map. Every function supports ordinary calls; a pipe injects its left value as the first argument.

This is the complete callable list. It excludes read-only special values such as args, env, and system, and does not mislabel read-only fields such as File.path or HttpResponse.status as functions.

14.2Core values, collections, environment, and control (22)

print

print(Value...) -> Null

Write values to stdout; a Stream is consumed and printed item by item.

print_error

print_error(Value...) -> Null

Write values to stderr for diagnostics.

exit

exit(Int?) -> Never

End the script with an optional status code (default 0) and unwind resources.

length

length(String | List | Map) -> Int

Return String code points or List/Map elements; use count for a Stream.

byte_length

byte_length(String | BytesBuffer) -> Int

Return the UTF-8 byte count of String or size of BytesBuffer.

type

type(Value) -> String

Return a value's logical type name.

is_type

is_type(Value, String) -> Bool

Test whether a value has the named logical type.

to_int

to_int(Int | Float | String) -> Int

Explicitly convert Int/Float/String to Int; invalid or overflowing input raises ValueError.

to_float

to_float(Int | Float | String) -> Float

Explicitly convert Int/Float/String to Float; invalid input raises ValueError.

get

get(List | Map | Record, Int | String) -> Value | Null

Safely read a List index, Map key, or record field; missing values return null.

require

require(Map, String) -> Value

Read a required Map key; missing raises KeyError, while a present null stays null.

pick

pick(Map, List<String>) -> Map

Return a new Map containing selected keys, preserving present null fields.

put

put(Map, String, Value) -> Map

Return a new Map with one key inserted or replaced; the original is unchanged.

remove_key

remove_key(Map, String) -> Map

Return a new Map without the named key.

append

append(List<T>, T) -> List<T>

Return a new List with one item appended.

remove_at

remove_at(List<T>, Int) -> List<T>

Return a new List without the indexed item; out of range raises IndexError.

now

now() -> DateTime

Return the current zoned DateTime.

datetime.parse

datetime.parse(String, String, String) -> DateTime

Parse DateTime using an explicit format and timezone; invalid input raises ValueError.

require_env

require_env(String) -> String

Read a required environment variable; missing raises KeyError.

sleep

sleep(Duration) -> Null

Wait for a Duration while remaining cancellable.

cancel

cancel() -> Never

Trigger the execution's root cancellation token and begin cleanup.

throw

throw(Error) -> Never

Throw an Error through the call stack or Flow.

14.3Flow and Stream (25)

Transformations such as map, where, and take stay lazy; terminals such as collect, count, and reduce consume the Stream. sort_by and group_by materialize input within resource limits. parallel uses bounded isolated workers and preserves output order.

stream

stream(List<T> | Map | Range) -> Stream<T>

Convert a List, Map entries, or Range into a lazy single-use Stream.

range

range(Int, Int) -> Stream<Int>

Create an Int Stream from start up to but excluding end.

map

map(Stream<T>, Function(T -> U)) -> Stream<U>

Lazily transform each item one-to-one without automatic flattening.

flat_map

flat_map(Stream<T>, Function(T -> Stream<U>)) -> Stream<U>

Return a child Stream per item and lazily concatenate child streams.

where

where(Stream<T>, Function(T -> Bool)) -> Stream<T>

Lazily retain items whose predicate returns Bool true.

take

take(Stream<T>, Int) -> Stream<T>

Lazily retain the first n items and close upstream early.

skip

skip(Stream<T>, Int) -> Stream<T>

Lazily discard the first n items and pass the remainder.

inspect

inspect(Stream<T>, Function(T -> Value)) -> Stream<T>

Run an observation closure for each item and pass the item unchanged.

distinct

distinct(Stream<Hashable>) -> Stream<Hashable>

Lazily remove duplicate hashable scalars while retaining a seen set.

sort_by

sort_by(Stream<T>, Map, Function(T -> Comparable)) -> Stream<T>

Materialize finite input and stably sort by closure key and asc/desc option.

group_by

group_by(Stream<T>, Function(T -> Hashable)) -> Stream<Group<T>>

Materialize finite input into Groups containing key and values.

debounce

debounce(Stream<T>, Duration) -> Stream<T>

Coalesce rapid events within a Duration, commonly for watch streams.

on_error

on_error(Stream<T>, Function(Error -> Stream<T>)) -> Stream<T>

On Stream failure, invoke a closure whose returned Stream supplies recovery output.

parallel

parallel(Stream<T>, Int, Function(T -> U)) -> Stream<U>

Process with at most n isolated workers, ordered output, bounded buffering, and fail-fast errors.

collect

collect(Stream<T>) -> List<T>

Consume a finite Stream and materialize it as a List.

count

count(Stream<T>) -> Int

Consume a Stream and return its item count.

first

first(Stream<T>) -> T | Null

Return the first item or null and close upstream early.

last

last(Stream<T>) -> T | Null

Consume a Stream and return its last item or null.

min

min(Stream<Number>) -> Number | Null

Consume a numeric Stream and return its minimum or null for empty input.

max

max(Stream<Number>) -> Number | Null

Consume a numeric Stream and return its maximum or null for empty input.

sum

sum(Stream<Number>) -> Number

Consume and sum a numeric Stream, respecting Int overflow rules.

reduce

reduce(Stream<T>, U, Function(State<T,U> -> U)) -> U

Fold a Stream from initial; the closure receives state with acc/item/index.

any

any(Stream<T>, Function(T -> Bool)) -> Bool

Return true on the first matching item and short-circuit upstream.

all

all(Stream<T>, Function(T -> Bool)) -> Bool

Return true only if every item matches; short-circuit on the first false.

for_each

for_each(Stream<T>, Function(T -> Value)) -> Null

Consume a Stream, execute a closure for each item, and return null.

14.4Text, Regex, JSON, and CSV (18)

contains

contains(String | List, Value) -> Bool

Test whether a String contains a substring or a List contains an equal value.

upper

upper(String) -> String

Return a new String converted to Unicode uppercase.

lower

lower(String) -> String

Return a new String converted to Unicode lowercase.

trim

trim(String) -> String

Remove whitespace from both ends of a String.

trim_start

trim_start(String) -> String

Remove leading whitespace from a String.

trim_end

trim_end(String) -> String

Remove trailing whitespace from a String.

starts_with

starts_with(String, String) -> Bool

Test whether a String starts with the given text.

ends_with

ends_with(String, String) -> Bool

Test whether a String ends with the given text.

replace

replace(String, String, String) -> String

Return a new String with matching text replaced.

split

split(String, String) -> List<String>

Split a String by delimiter text into List<String>.

join

join(List<String>, String) -> String

Join List<String> with delimiter text.

regex_match

regex_match(String, Regex) -> Bool

Test a String against a PCRE2 Regex under regex resource limits.

regex_captures

regex_captures(String, Regex) -> Map | Null

Return full match, byte positions, numbered and named captures; null when unmatched.

url_resolve

url_resolve(String, String?) -> Map

Resolve an absolute or relative HTTP(S) URL, remove fragments, default ports, and dot segments, and return host, path, and a stable fingerprint.

parse_json

parse_json(String) -> JsonValue

Strictly parse JSON String into ordinary HHY values with line/column errors.

encode_json

encode_json(JsonValue, Map?) -> String

Encode supported ordinary values as JSON; options may enable pretty output.

parse_csv

parse_csv(String | Stream<String>, Map?) -> Stream<Map>

Stream-parse a String or line Stream into Stream<Map>.

encode_csv

encode_csv(Stream<Map>, Map?) -> Stream<String>

Stream-encode Stream<Map> into CSV records without line terminators.

14.5Paths, files, and watch (15)

read_* functions read data; write_text, write_bytes, save_text, and save_lines use a sibling temporary file plus atomic replacement. append_text appends to the target. Dry-run intercepts filesystem actions.

path

path(String) -> Path

Lexically normalize String into Path without filesystem access.

path_join

path_join(Path, String | Path) -> Path

Combine a Path with a child path and return a normalized Path.

files

files(Path, String, Map?) -> Stream<File | Directory>

Lazily walk a root with a glob and return a File/Directory Stream.

read_text

read_text(Path) -> String

Read an entire UTF-8 file as String.

read_lines

read_lines(Path) -> Stream<String>

Lazily read UTF-8 lines with terminators removed.

read_bytes

read_bytes(Path) -> BytesBuffer

Read an entire binary file as BytesBuffer.

write_text

write_text(Path, String, Map?) -> Path

Atomically replace with String, supporting overwrite/create_parents.

append_text

append_text(Path, String) -> Path

Append String to the end of a file.

write_bytes

write_bytes(Path, BytesBuffer, Map?) -> Path

Atomically replace a file with BytesBuffer.

save_text

save_text(String | Stream<String>, Path, Map?) -> Path

Atomically save a String or pull a text Stream directly to disk.

save_lines

save_lines(Stream<String>, Path, Map?) -> Path

Write a String Stream with LF per item and atomically replace the target.

copy

copy(Path, Path, Map?) -> Path

Copy a file with atomic no-replace; the target parent directory must already exist.

move

move(Path, Path, Map?) -> Path

Move or rename a file while respecting overwrite options.

remove

remove(Path) -> Path

Remove an explicit Path and return it.

watch

watch(Path, Map?) -> Stream<FileEvent>

Return an infinite FileEvent Stream with recursive option and cancellation.

14.6Processes, standard input, and timers (6)

run passes argv directly without a shell; only shell explicitly uses shell parsing. Process launches obey timeout, output, and process-count limits.

run

run(List<String>, Map?) -> CommandResult

Execute argv directly without a shell and return CommandResult.

shell

shell(String, Map?) -> CommandResult

Explicitly execute a String through a shell for redirects, pipes, and shell syntax.

stdout_lines

stdout_lines(CommandResult) -> Stream<String>

Expose CommandResult.stdout as a lazy line Stream.

processes

processes() -> Stream<Process>

Return a Stream<Process> snapshot of current processes.

stdin_lines

stdin_lines() -> Stream<String>

Lazily read stdin lines until EOF or cancellation.

every

every(Duration) -> Stream<Int>

Produce an infinite timer tick Stream at a Duration interval.

14.7HTTP (10)

http.* only builds immutable request plans, timeout/retry transform a plan, and only send performs a network effect. response_body returns UTF-8 text; use response_bytes for binary data.

http.get

http.get(String, Map?) -> HttpRequest

Build a GET HttpRequest plan without network I/O.

http.post

http.post(String, Map?) -> HttpRequest

Build a POST HttpRequest plan without network I/O.

http.put

http.put(String, Map?) -> HttpRequest

Build a PUT HttpRequest plan without network I/O.

http.delete

http.delete(String, Map?) -> HttpRequest

Build a DELETE HttpRequest plan without network I/O.

timeout

timeout(HttpRequest, Duration) -> HttpRequest

Return a new HttpRequest with its timeout configured.

retry

retry(HttpRequest, Map) -> HttpRequest

Return a new HttpRequest configured with retry count and backoff.

send

send(HttpRequest) -> HttpResponse

Perform the HttpRequest network effect and return HttpResponse.

send_to

send_to(HttpRequest, Path) -> HttpResponse

Stream the HTTP body into an atomic file and return an HttpResponse with path and size.

response_body

response_body(HttpResponse) -> String

Validate response status and decode the bounded body as UTF-8 String.

response_bytes

response_bytes(HttpResponse) -> BytesBuffer

Validate response status and return the bounded binary BytesBuffer.

14.8Web Runtime (25)

web.* builds persistent applications, responses, middleware, and production endpoints; only web.listen binds a network port.

web.app

web.app() -> WebApp

Create a Web application.

web.get

web.get(WebApp, String, Function) -> WebApp

Register a GET route.

web.post

web.post(WebApp, String, Function) -> WebApp

Register a POST route.

web.put

web.put(WebApp, String, Function) -> WebApp

Register a PUT route.

web.patch

web.patch(WebApp, String, Function) -> WebApp

Register a PATCH route.

web.delete

web.delete(WebApp, String, Function) -> WebApp

Register a DELETE route.

web.use

web.use(WebApp, Function) -> WebApp

Append short-circuiting middleware.

web.static

web.static(WebApp, String, Path) -> WebApp

Mount a secure static directory.

web.cors

web.cors(WebApp, Map?) -> WebApp

Enable CORS and OPTIONS preflight.

web.request_id

web.request_id(WebApp) -> WebApp

Enable request IDs.

web.health

web.health(WebApp, String?) -> WebApp

Enable the health endpoint.

web.trust_proxy

web.trust_proxy(WebApp) -> WebApp

Trust the proxy-provided client address.

web.text

web.text(String, Map?) -> WebResponse

Create a text response.

web.html

web.html(String, Map?) -> WebResponse

Create an HTML response.

web.json

web.json(JsonValue, Map?) -> WebResponse

Create a JSON response.

web.redirect

web.redirect(String, Map?) -> WebResponse

Create a redirect response.

web.response

web.response(Map) -> WebResponse

Create a response from an options Map.

web.multipart

web.multipart(WebRequest, Path?) -> Map

Parse bounded multipart forms and uploads.

web.stream

web.stream(Stream, Map?) -> WebResponse

Create a backpressured streaming response.

web.sse

web.sse(Stream<String>) -> WebResponse

Create a Server-Sent Events response.

web.sign_cookie

web.sign_cookie(String, String) -> String

HMAC-sign a cookie value.

web.verify_cookie

web.verify_cookie(String, String) -> String | Null

Verify and read a signed cookie.

web.metrics

web.metrics(WebApp, String?) -> WebApp

Enable the Prometheus metrics endpoint.

web.gzip

web.gzip(WebApp) -> WebApp

Enable gzip negotiation through Accept-Encoding.

web.listen

web.listen(WebApp, Map?) -> Null

Start the bounded HTTP/1.1 server.

14.9Controlled concurrency (7, experimental and disabled by default)

atomic_state

atomic_state(data)

Create state owned by one Runtime/PID. Experimental; disabled by default.

atomic_read

atomic_read(state)

Read an immutable snapshot. Experimental; disabled by default.

atomic_update

atomic_update(state, callback)

Validate a pure callback candidate and publish synchronously; precommit errors preserve the old value. Experimental; disabled by default.

atomic_close

atomic_close(state)

Close the handle; later access and repeated close fail. Experimental; disabled by default.

atomic_file_update

atomic_file_update(path, timeout, callback)

Read and replace an existing regular file under a cooperative lock; timeout bounds lock waiting only. Experimental; disabled by default.

transaction_strict

transaction_strict(config, options?, callback)

Bind SQL to the current tx; caught errors still prevent commit; reconcile unknown commits. Experimental; disabled by default.

task_map

task_map(data, parallelism, result_limit, callback)

Execute isolated processes, return an ordered List, and bound direct children and accepted results. Experimental; disabled by default.

Read the complete guide and examples
Includes the guarantee matrix, failure handling and actual validation evidence
/en/learn/concurrency-scopes

Reference · 15

CLI Reference

Run, check, format, use the REPL, inspect dry-run plans, and profile performance.

15.1HHY 1.7.1 implementation and compatibility

1.7.1 passes local tests, four-platform CI and release validation. Language specification 1.0 remains frozen. Official sample, HTML and macOS/Linux Database 1.0.0 extensions are verified compatible. VS Code 0.2.0 and Sublime 0.1.0 retain independent versions; syntax and editor contract checks pass.

HIR, six optimization passes, integer MIR, type-feedback guards/deopt and local List scalar replacement are implemented. Explicitly enable HHY_COMPILER=ir, HHY_FEEDBACK_SPECIALIZATION=1 and HHY_SCALAR_REPLACEMENT=1; direct Bytecode remains default. Real workloads have not met default-enablement benefit gates. Scalar replacement retains original GC/quota reservations.

Optimization switches, verification and supported scope
v1.7.1 implementation
/docs/COMPILER_IR.md

1.7.1 release notes
Released capabilities and limits
/docs/RELEASE_NOTES_1.7.1.md

15.2Version and release identity

Use --version to confirm the binary version, project author, open-source license, and official contact details. Source builds use ./build/hhy; release archives or PATH installations can invoke hhy directly.

HHY · Version information · $ ./build/hhy --version
hhy 1.7.1
© 2026 HHY Language contributors
Author: houhuiyang
License: Apache License 2.0
https://hhylang.dev/
huiyang.hou@qq.com

Actual HHY 1.7.1 command output. The CLI reports the version, author, license, website, and contact address directly.

From an official archive, run ./bin/hhy --version in the extracted directory. After make install or adding HHY to PATH, run hhy --version.

15.3Complete command set

sh
hhy script.hhy [args...]
hhy run script.hhy [args...]
hhy run --engine ast|bytecode script.hhy [args...]
hhy repl
hhy check script.hhy...
hhy fmt script.hhy...
hhy fmt --check script.hhy...
hhy ast script.hhy
hhy bytecode script.hhy
hhy bytecode --metrics script.hhy
hhy tokens script.hhy
hhy run --dry-run script.hhy
hhy run --limit max_runtime=30s --limit max_memory=256mib script.hhy
hhy serve app.hhy [args...]
hhy serve --dev app.hhy [args...]
hhy serve --engine ast|bytecode --limit max_memory=256mib app.hhy -- [args...]
hhy profile script.hhy [args...]
hhy profile --engine ast|bytecode script.hhy [args...]
hhy profile --cpu script.hhy
hhy profile --heap --format json --output profile.json script.hhy
hhy check --format json script.hhy...
hhy contracts --format json
hhy install [--yes] [--dry-run] [--upgrade] [--locked] [--offline] [--lockfile FILE] [--cache DIR] [--registry DIR --trust-root FILE] <package-or-path>
hhy lock [--lockfile FILE] --registry DIR --trust-root FILE <package>
hhy fetch --locked [--lockfile FILE] [--cache DIR] --registry DIR --trust-root FILE
hhy rollback <package>
hhy doctor extensions [--lockfile FILE] [--cache DIR]
hhy list
hhy remove <package>
hhy --version
hhy -V
hhy --help
hhy -h
CommandPurpose
hhy contracts --format jsonExport callable contracts
hhy installInstall or upgrade extensions
hhy lockResolve dependencies and write lockfile
hhy fetch --lockedCache locked dependencies
hhy rollbackRestore the previous verified extension
hhy doctor extensionsVerify lockfile, cache and installed extensions
hhy list / hhy removeList or remove extensions
hhy check --format jsonEmit JSON diagnostics
hhy runRun a script and pass args
hhy run --engine ast|bytecodeSelect the AST fallback or Bytecode engine explicitly
hhy serveRun a persistent Web application configured by web.listen
hhy serve --devWatch source files and reload the Web application
hhy serve --engine ast|bytecodeSelect the execution engine used by Web handlers
hhy serve --limit NAME=VALUEApply repeatable Web Runtime resource limits
hhy profileAnalyze CPU hotspots, call counts, and managed-Heap allocations
hhy replStart the interactive environment
hhy checkCheck syntax and core semantics
hhy fmtWrite canonical formatting
hhy fmt --checkCheck formatting only
hhy astPrint the AST
hhy bytecodeCompile, verify, and disassemble Bytecode
hhy bytecode --metricsEmit cache-admission compile/verify/prepare metrics as JSON
hhy tokensPrint Lexer tokens
hhy run --dry-runPreview a redacted execution plan

hhy script.hhy is shorthand for hhy run script.hhy. Use -- after Runtime options when script arguments may begin with a dash.

15.4serve · Persistent Web applications

serve loads and checks an HHY Web application, then keeps the process alive. --dev safely reloads after source changes; --engine selects AST or Bytecode; repeatable --limit options bound Runtime resources. HTTP settings such as host, port, max_body, and workers remain explicit in web.listen, while trailing positional values are exposed through read-only args.

sh
hhy serve app.hhy
hhy serve --dev app.hhy -- 8080
hhy serve --engine bytecode \
  --limit max_memory=256mib \
  --limit max_runtime=30s \
  app.hhy -- 8080 production
Option/argumentBehavior
--devWatch the entry file and imported modules, then reload after changes
--engine ast|bytecodeSelect the handler engine explicitly; Bytecode is the default
--limit NAME=VALUEOverride memory, runtime, file, process, parallelism, HTTP-body, regex, or recursion limits; repeatable
--dry-runEvaluate the plan while blocking external effects; mainly useful for startup validation
--End Runtime options and pass every remaining value through args
serve does not guess an application port. The entry file must eventually call web.listen. Production deployments should run behind Caddy, Nginx, or a managed load balancer.

15.5Bytecode cache admission evidence

v1.7.1 adds read-only --metrics output for measuring real compile+verify and execution-plan verification cost. Across five fixed workloads and 21 paired fresh-process samples, compile+verify medians were 0.004–0.012 ms, only 0.0078%–0.1341% of cold-run wall time, below the joint 1 ms and 20% thresholds.

$ hhy bytecode --metrics examples/00-hello.hhy
{"bytecode_format_version":1,"compile_verify_ns":9000,"constants":21,"instructions":40,"schema_version":1,"source_bytes":167,"stream_kernel_version":1,"stream_kernels":1,"tool":"hhy bytecode --metrics","verify_prepare_ns":4000}
No process or disk Bytecode cache is enabled, and third-party precompiled Bytecode remains rejected. Any future admission requires new performance evidence plus a complete source/dependency/version/feature/target/security fingerprint, checksum, bounded decoding, the full Verifier, and execution-plan verification.

15.6CPU and Heap profiling

profile executes the script and collects CPU and managed-Heap data in the same run by default. Reports go to stderr, leaving script stdout unchanged, and the command preserves the script's exit code.

sh
hhy profile examples/09-profile-algorithms.hhy -- fibonacci 20
hhy profile --cpu examples/09-profile-algorithms.hhy fibonacci 20
hhy profile --heap --format json --output profile.json examples/09-profile-algorithms.hhy fibonacci 20
OptionBehavior
--cpuCollect only 1ms process-CPU samples and call counts
--heapCollect only cumulative allocations, allocation count, Heap peak, and post-GC usage
--format text|jsonSelect a human- or machine-readable report; default: text
--output <path>Write the report to a file instead of stderr
--limit NAME=VALUEOverride Runtime resource limits, as with run
--dry-runBlock external effects, as with run, and profile plan execution
$ hhy profile --engine bytecode examples/09-profile-algorithms.hhy -- fibonacci 20
HHY profile: examples/09-profile-algorithms.hhy

Summary
  Engine           bytecode
  Wall time        0.013 s
  CPU time         0.008 s
  CPU utilization  58.8%
  CPU samples      4
  Heap peak        1.8 MiB
  Heap after GC    92.0 KiB
  Allocated        1.7 MiB
  Allocations      33040

CPU hotspots
  CPU%    Samples      Calls  Function
   75.0%        3      21891  fibonacci  examples/09-profile-algorithms.hhy:5:1
   25.0%        1          1  print  examples/09-profile-algorithms.hhy:18:6
    0.0%        0          1  <bytecode-top-level>  examples/09-profile-algorithms.hhy:1:1
    0.0%        0          1  length  examples/09-profile-algorithms.hhy:12:10
    0.0%        0          1  to_int  examples/09-profile-algorithms.hhy:17:19
  Note: fewer than 10 CPU samples; use a larger workload for stable results.

Allocation hotspots
  Bytes          Objects  Function
  1.7 MiB            32856  fibonacci  examples/09-profile-algorithms.hhy:5:1
  11.6 KiB              184  <bytecode-top-level>  examples/09-profile-algorithms.hhy:1:1

fibonacci 6765
HHY 1.7.0 · 2026-09-08 · macOS arm64 · single local run; fewer than 10 CPU samples, so timings and CPU percentages vary between runs. Default compiler settings; no opt-in optimization flags.
CPU profiling samples process CPU time, so file, HTTP, and process waits are not misreported as CPU hotspots. Scripts that finish in a few milliseconds may need a larger or repeated workload. Heap metrics cover memory managed by HHY's Boehm GC, not extension subprocesses or memory owned directly by native libraries.

15.7Interpreter performance evolution

v1.7.1 defaults to the Compiler/Verifier-validated Bytecode VM. Native Opcode dispatch, static slots, reusable call frames, and low-allocation closure fast paths reduce CPU cost; the AST Interpreter remains the permanent semantic oracle and explicit fallback.

v1.7.1 / RUNTIME ARCHITECTURE

From source to execution

Solid: default pathDashed: opt-in
01Compile-time verification
Source→AST→Bytecode
Compiler
→Verifier→Bytecode
↓↓

Structured HIR

Off by default

AST → six passes → Bytecode

Independent verification after each pass; size-budget failure falls back to direct compilation.HHY_COMPILER=ir

Integer MIR plans

Off by default

Integer expressions → register MIR → verification

Available with either compiler path; delivered to the VM with Bytecode.HHY_FEEDBACK_SPECIALIZATION=1
↓Verified Bytecode / optional MIR plans
02Runtime feedback
Bytecode VM
Opcode dispatch · slots · call frames
Optional specializationOnly with a verified MIR plan
Type feedback→Guard every entry→
PassInteger MIR → boxed result
Not ready / mismatchGeneric Bytecode path
↳
AST InterpreterDirect from AST · independent semantic oracle · explicit fallback
--engine ast
Optimization switches and semantic guarantees+

Six passes: folding, constant propagation, copy propagation, peephole, unreachable removal and DCE. Unsupported shapes or mismatches use the generic path. Arithmetic failures restore original-operation errors without replaying effects; repeated mismatches disable feedback.

HHY_SCALAR_REPLACEMENT=1
Separately enables local List scalar replacement, retaining GC/quota reservations without eliminating physical heap allocations.

HIR / MIR are off by default. Compile-time verification and runtime guards are independent; real workloads have not met default-enablement gates.
v1.7.1 defaults run, profile, and script shorthand to Bytecode. Use --engine ast or HHY_ENGINE=ast for immediate rollback. Engine and top-level labels identify the actual execution path.

15.8Enable HIR / integer MIR

These are environment variables, not hhy subcommands. The commands below use macOS/Linux sh, bash or zsh; a prefix affects only that invocation. Use hhy after installation, or ./build/hhy in a source checkout. Save this file first.

optimizer-demo.hhy
fn calculate(x) { return x * 2 + 1 }
fn first(x) { return [x + 1, x * 2][0] }
for i in 0..32 {
    calculate(i)
    first(i)
}
print(calculate(20))
print(first(40))
sh
# HIR only
HHY_COMPILER=ir hhy run --engine bytecode optimizer-demo.hhy

# Integer MIR only (direct compiler)
HHY_FEEDBACK_SPECIALIZATION=1 hhy run --engine bytecode optimizer-demo.hhy

# HIR + integer MIR + local List scalar replacement
HHY_COMPILER=ir HHY_FEEDBACK_SPECIALIZATION=1 HHY_SCALAR_REPLACEMENT=1 hhy run --engine bytecode optimizer-demo.hhy
Expected output: two lines of 41. The loop accumulates feedback; enabling a flag does not specialize every function. Only bounded straight-line integer expressions qualify; captures, dynamic calls and unsupported shapes use the generic path. HIR and MIR are independent; scalar replacement also requires MIR.
sh
# HIR report goes to stderr
HHY_COMPILER=ir HHY_COMPILER_REPORT=1 hhy bytecode --metrics optimizer-demo.hhy

# Runtime hits and deoptimizations go to profile.json
HHY_COMPILER=ir HHY_FEEDBACK_SPECIALIZATION=1 HHY_SCALAR_REPLACEMENT=1 hhy profile --engine bytecode --heap --format json --output profile.json optimizer-demo.hhy

For HIR, the stderr report has compiler=structured-ir; inspect passes and size_fallback. For MIR, typed_plans in Bytecode metrics counts compiled plans; typed_specialization.enabled and sites[].hits in profile.json show enablement and actual hits. scalar_reservations counts scalar reservations. On 2026-09-08, this example produced 26 hits per function and 26 scalar reservations for the List function; timings vary by host.

sh
# Disable individual HIR passes; HIR remains enabled
HHY_COMPILER=ir HHY_COMPILER_DISABLE=fold,dce hhy run optimizer-demo.hhy

# Disable every HIR pass; HIR remains enabled
HHY_COMPILER=ir HHY_COMPILER_DISABLE=all hhy run optimizer-demo.hhy

# Restore the default compiler and disable MIR/scalar replacement (macOS/Linux)
unset HHY_COMPILER HHY_COMPILER_DISABLE HHY_COMPILER_REPORT HHY_FEEDBACK_SPECIALIZATION HHY_SCALAR_REPLACEMENT
hhy run --engine bytecode optimizer-demo.hhy
HHY_COMPILER_DISABLE=all disables HIR passes, not the HIR compiler or MIR. HHY_FEEDBACK_SPECIALIZATION=0 and HHY_SCALAR_REPLACEMENT=0 disable their Runtime optimizations. --engine ast selects the independent AST fallback.

In Windows PowerShell, use this environment-variable syntax; remove the variables afterward to restore defaults.

text
$env:HHY_COMPILER="ir"
$env:HHY_FEEDBACK_SPECIALIZATION="1"
$env:HHY_SCALAR_REPLACEMENT="1"
hhy run --engine bytecode optimizer-demo.hhy
Remove-Item Env:HHY_COMPILER, Env:HHY_FEEDBACK_SPECIALIZATION, Env:HHY_SCALAR_REPLACEMENT

15.9Runtime resource limits

The run command accepts repeatable --limit NAME=VALUE options. Sizes require b/kb/mb/gb/kib/mib/gib, durations require ns/us/ms/s/min/h, and counts have no unit.

sh
hhy run --limit max_runtime=30s --limit max_memory=256mib script.hhy
LimitDefault
max_memory512mib
max_open_files256
max_processes16
max_parallelism16
max_http_body16mib
max_regex_steps1000000
max_recursion256
max_runtime0 (no total CLI limit)

15.10Stable exit codes

text
0  success
1  unhandled runtime error
2  syntax or static-check error
3  invalid CLI usage
4  file I/O, process, or network error
5  timeout or cancellation

Automation should branch on stable exit codes rather than error text.

Extensions · 16

Extension System

The v1.7.1 process-extension model: the signed official Registry, source builds, manifests, capabilities, Protocol 1, and callable registration.

16.1HHY 1.7.1 implementation and compatibility

1.7.1 passes local tests, four-platform CI and release validation. Language specification 1.0 remains frozen. Official sample, HTML and macOS/Linux Database 1.0.0 extensions are verified compatible. VS Code 0.2.0 and Sublime 0.1.0 retain independent versions; syntax and editor contract checks pass.

HIR, six optimization passes, integer MIR, type-feedback guards/deopt and local List scalar replacement are implemented. Explicitly enable HHY_COMPILER=ir, HHY_FEEDBACK_SPECIALIZATION=1 and HHY_SCALAR_REPLACEMENT=1; direct Bytecode remains default. Real workloads have not met default-enablement benefit gates. Scalar replacement retains original GC/quota reservations.

Optimization switches, verification and supported scope
v1.7.1 implementation
/docs/COMPILER_IR.md

1.7.1 release notes
Released capabilities and limits
/docs/RELEASE_NOTES_1.7.1.md

16.2The current extension boundary

v1.7.1 implements local install/list/remove, an Ed25519-signed Registry, manifest and SHA-256 validation, isolated-process handshakes, dynamic callable registration, synchronous calls, structured errors, and shutdown. Scripts can directly import installed packages.
CapabilityCurrent statusBoundary
Extension distributionImplementedSigned official Registry or local source builds; identity, target, signatures, and file hashes are verified before installation
Process protocolImplementedhandshake, register, call, call_result, error, shutdown
Value transportImplementedJSON protocol mapping for Null, Bool, numbers, String, List, and Map
DB Stream / handle / cancelImplemented for DB 1.0Negotiated scoped_resources, scoped handles and cancellation; DB Streams pull cursors rather than providing generic Stream transport
Public Native ABINot committedEvaluate only if measurements show the process model is insufficient

A package name is its top-level namespace: package_name may register only package_name.* and cannot replace hhy.*, std.*, core callables, or another package. Importing an uninstalled package raises ModuleNotFoundError.

16.3Available official extensions

ExtensionVersionPublishedStatusCapability
database1.0.02026-09-07ReleasedMySQL/PostgreSQL pools, remote TLS, transactions, prepared statements, cursors and exact types
html0.2.02026-09-01ReleasedLexbor CSS selectors, text/attribute reads, and structured extraction

Database Extension Guide
Install database 1.0.0 for pools, remote TLS, transactions and streaming queries.
/en/learn/database-extension

HTML Extension and Crawler Framework
Use html 0.2.0 with observable batch extraction, URL normalization, a safe frontier, deduplication, and SSRF protection.
/en/learn/html-crawler-framework

16.4Where to get extensions

SourceBest forAddress or action
Official HHY RegistryDownloading signed official extensions for a specific platformhttps://registry.hhylang.dev (index: /index.json; trust root: /root.json)
GitHub sourceReviewing code, auditing changes, or building from sourcehttps://github.com/hh696-wq/hhy-vm/tree/main/extensions
Local source buildExtension development or a custom local dependency combinationmake -C extensions/<name>, then install the local directory
sh
# Build and install from source (html example)
git clone https://github.com/hh696-wq/hhy-vm.git
cd hhy-vm
make
make -C extensions/html
./build/hhy install ./extensions/html

# Inspect installed extensions
./build/hhy list

Open the official extension index ↗
One extension version may contain darwin-arm64, linux-x86_64, linux-arm64, and windows-x86_64 targets; the installer selects only its native target.
https://registry.hhylang.dev/index.json

Browse extension source on GitHub ↗
Source, hhy.toml manifests, tests, and build scripts for sample, html, and database.
https://github.com/hh696-wq/hhy-vm/tree/main/extensions

Database 1.0.0 has an independent GitHub Release and is bundled with macOS/Linux HHY archives. Windows Runtime archives exclude DB. Registry versions depend on the signed index.

16.5Install, list, and remove

sh
./build/hhy install ./path/to/extension
./build/hhy list
./build/hhy remove package-name
StepActual behavior
installRead hhy.toml; validate package name, author, requires_hhy, protocol, command, and integrity; display capabilities and ask the user to confirm
import / loadRecheck installed SHA-256 data, start the extension process, handshake, and register callables
listDisplay each installed package's name, version, author, protocol, and declared capabilities
removeDelete the local package record and installation directory; subsequent imports fail
The default extension home is ~/.hhy/extensions; set HHY_EXTENSION_HOME for isolated CI or tests. Capabilities are reviewable declarations, not a general operating-system sandbox. Treat third-party extensions as native executables.

16.6Generic hhy.toml manifest

text
[package]
name = "package-name"
version = "0.1.0"
author = "Your Organization"
requires_hhy = ">=1.1,<2.0"

[extension]
kind = "process"
command = "bin/hhy-package"
protocol = "1"

[capabilities]
read = []
write = []
network = []
process = false
FieldDeveloper constraint
package.nameUnique top-level namespace using lowercase letters, digits, and hyphens
package.authorShown during install and list to identify official or third-party provenance
requires_hhyRuntime version range checked by the installer
extension.commandMust be an executable under the package bin/ directory and cannot escape its root
extension.protocolCurrently accepts Protocol 1
capabilitiesDeclares file, network, and subprocess access for review

16.7How an extension loads

Extension loading flow

HHY script → Runtime validation → isolated extension process → protocol registration → call execution → structured result or error.

StageRuntime and extension responsibility
resolveRuntime resolves import package_name to an installed package, parses its manifest, and validates command integrity
spawnRuntime starts a separate process with --protocol 1 and opens stdin/stdout protocol pipes
handshakeBoth sides confirm extension_id and protocol_version=1.0
registerThe extension sends one registration message; Runtime validates its package namespace and contracts
callRuntime sends serializable arguments; request_id correlates each call and call_result
shutdownRuntime sends shutdown and reaps protocol streams and the child process
Runtime calls remain synchronous. HHY 1.5.0 adds scope-end and cancellation messages for DB extensions declaring scoped_resources. Handles are request-scoped resource tokens; host DB Streams pull cursor batches. Generic Stream credit and a public Native ABI remain unavailable.

16.8What an extension author must implement

PartRequirement
PackageProvide hhy.toml, an in-package executable command, and SHA-256 integrity data verifiable by the installer
StartupAccept only --protocol 1; write protocol messages only to stdout and logs to stderr
HandshakeValidate extension_id and protocol_version and return matching identity
RegisterSend exactly one initial registration; every name must stay in the package namespace and provide a valid contract
CallReturn call_result or structured error for each request_id without exposing credentials or sensitive diagnostics
ShutdownIdempotently release connections, memory, and other extension resources
TestsCover identity mismatch, invalid arguments, extension exit, protocol errors, and resource cleanup

The extension process does not receive the complete host environment; Runtime passes only arguments explicitly supplied by the script. Errors should remain actionable without including passwords, tokens, complete connection addresses, or other sensitive information.

Extensions · 17

Database Extension Guide

Database 1.0.0: MySQL/PostgreSQL pools, remote TLS, transactions, prepared statements, cursors, exact types and HHY Streams. Requires HHY 1.5.0 or newer; compatibility with 1.7.0 is verified.

17.11.7.1 controlled concurrency and atomicity

This release fixes resource lifetimes and delivers seven experimental APIs, disabled by default. Review state ownership, cooperative file locks, strict transactions and bounded-task guarantees before enabling them.

Read the complete guide and examples
Includes the guarantee matrix, failure handling and actual validation evidence
/en/learn/concurrency-scopes

17.2Version and installation

DB 1.0.0 shipped with HHY 1.5.0 on 2026-09-07 and is independently versioned. DB supports macOS arm64 and Linux arm64/x86_64; Windows Runtime archives exclude DB.

Download Database 1.0.0
HHY 1.5.0 · Database 1.0.0
https://github.com/hh696-wq/hhy-vm/releases/tag/database-v1.0.0

sh
hhy --version
# Extract the matching release archive and preserve bin/ and lib/
hhy install ./hhy-database-1.0.0-darwin-arm64
hhy list

# Or build from the repository root
make -C extensions/database
./build/hhy install ./extensions/database

17.3Data sources and remote TLS

config.local.json
{
  "driver": "mysql",
  "host": "127.0.0.1",
  "port": 3306,
  "user": "cms_test",
  "password": "CHANGE_ME",
  "database": "hhy_test",
  "allow": [
    "127.0.0.1:3306"
  ],
  "tls": "disabled",
  "max_open": 4,
  "max_idle": 2,
  "connect_ms": 5000,
  "acquire_ms": 1000,
  "timeout_ms": 30000
}
This example targets a dedicated local fixture. Protect the config file and exclude it from Git; read production credentials from the environment or a secret source. Remote sources require a map with the real host, exact host:port in allow, verify_identity TLS and a CA path. Deployment owns VPC, allowlists and database grants; real RDS acceptance remains pending.

Legacy URLs remain supported for loopback. Use maps for remote endpoints and pool options. MySQL uses ? and PostgreSQL uses $1, $2; identifiers cannot be value parameters. The allow list is trusted-script policy, not an OS sandbox.

17.4Pools and queries

query.hhy
import database
let cfg = read_text(path(args[0])) |> parse_json
database.query(cfg, "SELECT ? AS value", [42], { typed: true }) |> print
database.stats() |> print
sh
hhy run query.hhy ./config.local.json

Pools are isolated by data source and extension process. Defaults are max_open=4 and max_idle=2; add worker budgets together. Idle/lifetime retirement, resets, acquire timeouts and metrics are supported. Duration options such as timeout_ms: 5s work directly. Results default to String/null; typed mode preserves numeric types, exact envelopes preserve decimals/large integers and binary maps to BytesBuffer.

17.5Transactions and streaming

The examples require an existing pages(id, title) fixture; replace placeholders for PostgreSQL. Transaction callbacks commit on success and roll back on error. MySQL DDL is not a multi-step atomic transaction; installers must track migration checkpoints.
transaction.hhy
import database
let cfg = read_text(path(args[0])) |> parse_json
cfg |> database.with_transaction { tx ->
    database.execute(tx, "UPDATE pages SET title = ? WHERE id = ?", ["Hello HHY", 1])
    database.query(tx, "SELECT id, title FROM pages WHERE id = ?", [1])
} |> print
stream.hhy
import database
let cfg = read_text(path(args[0])) |> parse_json
database.stream(cfg, "SELECT id, title FROM pages ORDER BY id", [])
    |> take(100)
    |> collect
    |> print

Streams pull batches; take closes results early. Handles cannot cross requests/workers or survive close; request completion cleans abandoned resources. Unread results can discard a physical connection, so early stream closure inside a transaction may invalidate that transaction.

17.6API and limits

APIPurpose
ping / query / executeConnectivity, queries and writes
connection / begin / commit / rollback / closeScoped connections and transactions
with_transaction / savepoint / rollback_to / release_savepointTransaction callbacks and savepoints
prepare / query_prepared / execute_preparedReuse server statements on one connection
transaction / execute_manyAtomic batches of 1–100 DML statements
cursor / fetch / next_result / close_result / streamIncremental results; next_result supports MySQL
stats / capabilitiesMetrics and version capabilities
Fields are limited to 64 KiB and protocol messages to 1 MiB. Bounded query returns at most 10000 rows and reports truncation. Saturation returns DB_POOL_TIMEOUT; cancellation/timeouts may discard connections. DB_COMMIT_UNKNOWN means the commit outcome is unknown: never automatically replay writes.

17.7Acceptance and installation scenarios

MySQL 8.4/PostgreSQL 17 pass local/CI, AST/Bytecode, two-worker, TLS positive/negative, cancellation and sanitizer acceptance, plus local million-row streaming. An install.hhy database fixture covers failure recovery, repeat installation, failed upgrade rollback and backup restoration; it is not a full CMS. Local sustained tests ran five minutes per backend, not a 24-hour soak or RDS failover certification.

Full API and migration guide
HHY 1.5.0 · Database 1.0.0
/docs/DATABASE_1.0.0_README.md

Measurements and reproduction
HHY 1.5.0 · Database 1.0.0
/docs/DATABASE_1.0.0_ACCEPTANCE.md

View passing release Actions
HHY 1.5.0 · Database 1.0.0
https://github.com/hh696-wq/hhy-vm/actions/runs/34079510695

Extensions · 18

HTML Extension and Crawler Framework

Combine the official HTML extension with a safe frontier for URL normalization, discovery, recursive deduplication, and bounded static crawling.

18.1What the HTML extension is

The official html 0.2.0 package is a process extension with no network or filesystem effects. Lexbor parses untrusted HTML, evaluates CSS selectors, and extract_report returns batch rows with explicit truncation metadata.

The extension parses and extracts only. It does not fetch URLs, schedule pages, or return DOM handles. HHY Runtime and the crawler layer own HTTP, TLS, timeouts, retries, and security policy.

18.2Build and install the HTML extension

sh
brew install jansson lexbor
make -C extensions/html
./build/hhy install ./extensions/html
./build/hhy list
Complete signaturePurpose
html.text(String html, String selector, Map?) -> String?Read normalized text from the first matching node
html.text_all(String html, String selector, Map?) -> List<String>Read text from every matching node
html.attr(String html, String selector, String name, Map?) -> String?Read an attribute from the first matching node
html.attr_all(String html, String selector, String name, Map?) -> List<String>Read an attribute from every matching node
html.exists(String html, String selector) -> BoolTest whether a selector matches
html.extract(String html, String selector, Map schema, Map?) -> List<Map>Parse once and project repeated records through a schema
OptionCallablesBehavior
trim: Booltext, text_all, attr, attr_allTrim surrounding whitespace; enabled by default
max_results: Inttext_all, attr_all, extractDefaults to 1000; hard limit 10000

The extension limits input to 768 KiB, keeping protocol messages bounded. extract schema fields use { selector, value: "text" } or { selector, value: "attr", name }; an empty selector reads the current root. It does not return DOM handles: Protocol 1 transports only JSON-shaped values, so html.extract performs parsing and projection inside one extension call.

18.3From HTML extraction to a safe crawler

The static spider composes separate responsibilities: Runtime url_resolve normalizes relative references; the HTML extension discovers links; a depth frontier checks domains, paths, page and queue budgets, and fingerprints before admission; the HTTP layer blocks SSRF at the resolved connection address.

CapabilityCurrent implementation
url_resolve(url, base?)Returns url, scheme, host, port, path, query, and a stable fingerprint
Link discoveryfollow_selector extracts href values and feeds the next frontier
Frontier and deduplicationDepth batches with bounded concurrency and pre-admission fingerprint deduplication
Hard boundariesDomains, path prefixes, depth, pages, frontier size, and links per page
SSRF protectionReject loopback, private, and link-local socket addresses, including redirects

18.4Scope and explicit boundaries

The crawler supports an in-memory or atomically checkpointed frontier, strict configuration-matched resume, and send_to response files. An optional Playwright renderer executes JavaScript separately from the side-effect-free Lexbor extension and applies domain plus DNS private-network checks to documents, redirects, and subresources.

Do not use it to bypass robots.txt, authentication, CAPTCHAs, or anti-bot controls. Crawl only authorized sites with an identifiable User-Agent, conservative concurrency, and explicit page budgets.

Roadmap · 19

Language and VM Evolution Roadmap

v1.7.1 acceptance history and major-release plans for atomic state, shared memory across workers, coroutines and distributed transactions.

19.11.7.1 controlled concurrency and atomicity

This release fixes resource lifetimes and delivers seven experimental APIs, disabled by default. Review state ownership, cooperative file locks, strict transactions and bounded-task guarantees before enabling them.

Read the complete guide and examples
Includes the guarantee matrix, failure handling and actual validation evidence
/en/learn/concurrency-scopes

19.2HHY 1.7.1 implementation and compatibility

1.7.1 passes local tests, four-platform CI and release validation. Language specification 1.0 remains frozen. Official sample, HTML and macOS/Linux Database 1.0.0 extensions are verified compatible. VS Code 0.2.0 and Sublime 0.1.0 retain independent versions; syntax and editor contract checks pass.

HIR, six optimization passes, integer MIR, type-feedback guards/deopt and local List scalar replacement are implemented. Explicitly enable HHY_COMPILER=ir, HHY_FEEDBACK_SPECIALIZATION=1 and HHY_SCALAR_REPLACEMENT=1; direct Bytecode remains default. Real workloads have not met default-enablement benefit gates. Scalar replacement retains original GC/quota reservations.

Optimization switches, verification and supported scope
v1.7.1 implementation
/docs/COMPILER_IR.md

1.7.1 release notes
Released capabilities and limits
/docs/RELEASE_NOTES_1.7.1.md

19.3Language / VM Evolution Roadmap

Current v1.7.1 · Database 1.0.0 released alongside the complete Web Runtime

ReleaseRelease dateMain changesAcceptance status
v1.0.0 · Released2026-08-25Core language and VM semantics frozenPipe, Value, Stream, Error, the core standard library, and three-platform release evidence completed
v1.1.0 · Released2026-08-26Local process extensions and the official database extensionInstall/load integrity, synchronous Protocol 1 calls, database 0.2.0, and three-platform release evidence completed
v1.1.1 · Released2026-08-27Performance optimization and resource-boundary stabilityhhy profile, interpreter hotspot baselines, and Runtime resource boundaries completed
v1.1.2 · Released2026-08-27HTML extension and static collector frameworkThree-platform CI, protocol tests, local fixtures, and the real hhylang.dev crawl completed
v1.1.3 · Released2026-08-28Runtime correctness and performance hardeningGC pressure regression, sanitizers, hash indexes, stable diagnostics, and three-platform release evidence completed
v1.1.4 · Released2026-08-28Safe static spiderURL normalization, discovery, frontier, limits, fingerprint deduplication, and connection-level SSRF protection
v1.1.5 · Released2026-08-30Resumable spider and browser renderingPersistent frontier, resume, streamed files, optional Playwright, and Windows MSYS2 build evidence
v1.1.6 · Completed2026-08-31Stable engineering baselineHost capability probes, layered CI, machine-readable performance baselines, and release consistency gates
v1.1.7 · Completed2026-08-31Diagnostics and editor baselineVersioned JSON diagnostics, Contract Registry JSON, a minimal LSP, and a VS Code editing loop
v1.1.8 · Completed2026-08-31Gradual Runtime governanceFirst module boundary, internal ownership API, sanitizer/GC stress, and a blocking performance-regression gate
v1.2.0 · Released2026-08-31Official extension distribution and signingNamespaced identities, Ed25519-signed index and package descriptors, deterministic resolution, dry runs, and transaction-safe installs
v1.2.1 · Released2026-09-01Locking, offline installs, and safe rollbackThe same lock produces the same graph; offline rebuilds work; failed upgrades preserve the old environment
v1.2.2 · Released2026-09-01Official HTML complex-extension validationReal fixtures, observable truncation, structured errors, and four-platform distribution all pass
v1.3.0-alpha · Prereleased2026-09-01Bytecode compiler skeletonCore syntax compiles, invalid Bytecode is rejected, and AST remains the default engine
v1.3.0-beta · Stage gate completeMerged into v1.3.0Bytecode VM execution coreThe execution bridge, verifier, resource boundaries, and full dual-engine fixtures passed acceptance
v1.3.0-rc · Stage gate completeMerged into v1.3.0Performance, profiler, stack trace, and default-switch gatesThree-platform and failure evidence is complete; the CPU benefit gate failed, so AST remains the default
v1.3.0 · Released2026-09-01Opt-in production Bytecode pathThe full suite passes both engines; the performance decision retains AST as default with explicit fallback
v1.3.1 · Released2026-09-01Real-workload compatibility hardeningThe official workload dual-engine matrix and capability evidence passed three-platform CI
v1.3.2 · Released2026-09-01VM internal-boundary stabilizationVersioned Bytecode Runtime boundary, static governance, and continuous AST oracle
v1.3.3 · CompletedMerged into v1.3.5Native Opcode executionNormal Bytecode execution no longer calls the AST evaluator; full dual-engine semantics pass
v1.3.4 · CompletedMerged into v1.3.5VM data-path optimization1M CPU workload ratio is 0.6805 with no material short-script or JSON/I/O regression
v1.3.5 · Released2026-09-01Bytecode default enginerun, profile, and shorthand default to Bytecode; AST remains the permanent oracle and explicit fallback
v1.3.6 · Released2026-09-01Stream Int fusion performance closureProvably safe shapes fuse conservatively; unknown shapes fall back losslessly; dual-engine and cross-platform gates pass
v1.3.7 · Released2026-09-01Specialization hardeningNamed metadata, unified stack/error rules, fallback reasons, and three-path differential gates pass
v1.3.8 · Released2026-09-01Compiler/Verifier optimization IRRuntime does not inspect AST shapes; Stream Kernels verify independently and fall back safely
v1.3.9 · Released2026-09-01Profiler and resource consistencyNormal and observed execution share decisions; overhead, cancellation, and Heap-attribution gates pass
v1.3.10 · Released2026-09-01Bytecode cache governanceFive-workload evidence misses admission thresholds; no cache and no unverified external Bytecode
v1.3.11 · Released2026-09-02Runtime error-path hardeningStructured errors publish only after full construction; the original fuzz input is retained as a permanent regression
v1.4.0 · Completed2026-09-03Web Runtime foundationEmbedding, one-time application loading, reusable handlers, and isolation gates complete
v1.4.1 · Completed2026-09-03Web MVPHTTP/1.1, Router, Query/Header/Cookie, JSON APIs, and stable errors complete
v1.4.2 · Completed2026-09-03Web application capabilitiesMiddleware, static files, uploads, CORS, signed cookies, gzip, and reload complete
v1.4.3 · Released2026-09-03Streaming and multi-workerSSE, Stream, large-file Range, processes, and observability complete; one million requests with zero failures
v1.5.0 · Released2026-09-07Database 1.0.0 and Runtime resource integrationPools, remote TLS, transactions, cursors, exact types, cancellation and worker isolation; dual-database/engine and release Actions pass; RDS validation pending
v1.6.0 · Evaluated2026-09-08Instruction selection and dispatch profilesVersioned opcode/pair/triple metrics and switches ship in v1.7.0; superinstructions did not meet admission gates
v1.6.1 · Implemented2026-09-08Calls, closures and unwindCompact call layout, closure paths and exception-region experiments pass differential checks; opt-in, shipped in v1.7.0
v1.6.2 · Evaluated2026-09-08Conditional inline cachesMap-slot experiment and stable/changing call-site evaluation completed; generic fallback retained, not admitted by default; shipped in v1.7.0
v1.6.3 · Evaluated2026-09-08GC, memory and scheduling decisionsAllocation/pause and scheduling demand evaluated; no new GC or async scheduler admitted; decisions delivered in v1.7.0
v1.7.0 · Released2026-09-08Optimizing Compiler & RuntimeFour-platform CI and release validation pass; HIR, six passes, integer MIR, guards/deopt and local List scalar replacement ship opt-in
v1.7.1 · Current2026-09-14Resource ownership fixes and seven opt-in concurrency APIs. Remaining 1.7.x work stays subject to acceptance.Passed local Release and ASan/UBSan suites, 69 engine/configuration cases, MySQL/PostgreSQL idempotency and unknown-commit reconciliation, a 120-second soak, 100,000-request Web test, four-platform CI and release validation. Seven experimental APIs remain disabled by default; a 24-hour soak, real power-loss testing and the complete fault matrix remain pending.

19.4Future plans

The roadmap prioritizes atomicity, shared state, coroutines and distributed transactions, grouped by major release series. These are planned goals; unfinished capabilities are not guarantees of 1.7.1.

Major seriesGoalMilestonesAvailability and acceptance
1.x · Stabilize existing capabilitiesStabilize the seven APIs and establish ownership, effect and resource contracts for shared state and coroutines.Owner-bound atomic state → cooperative file updates → strict database transactions and bounded tasks; remove experimental gates separately after acceptance.24-hour soak on the candidate build, fault matrix, AST/Bytecode parity, platform and resource validation; failed candidates remain experimental.
2.x · Atomic state and coroutine kernelDeliver domain-wide language atomicity, atomic shared-memory state across workers, and a coroutine scheduler.Shared arena and generation handles → multi-object read/write sets and unified commit → resumable frames, nonblocking I/O and structured cancellation → integrated stabilization.Transaction-history checks, crash recovery, GC roots, no lost wakeups, fairness, resource bounds and legacy-mode compatibility; stabilize the new language mode only after acceptance.
3.x · Distributed transactionsExtend consistency to cross-node participants that support the transaction protocol.Durable coordinator → prepare/commit/rollback adapters → UNKNOWN queries and recovery → high availability, fault injection and operational tooling.Participant crashes, partitions, duplicate messages, decision logs and recovery; stabilize only validated participant configurations, with infrastructure still explicitly configured.
“Global atomicity” means unified multi-object transactions over managed shared state within one StateDomain. It does not automatically roll back ordinary variables, HTTP, files and databases together. Shared memory initially covers workers on one host; distributed transactions cover protocol-capable participants, and compensation is not atomic rollback.
Disabled featureWhen it becomes availableMeaning of availability
Seven existing experimental APIsDuring 1.x stabilization, separately after acceptance for state, files, databases and tasks.Callable without HHY_CONCURRENCY_EXPERIMENTS; APIs remain explicit and ordinary variables keep their semantics.
Cross-worker atomic state and coroutinesExperimental first in 2.x; stable after joint correctness, recovery and resource acceptance.The new mode becomes stable; existing programs do not automatically change sharing or scheduling semantics.
Distributed transactionsIn 3.x after durable coordination and participant acceptance.Stable APIs still require database/coordinator configuration and do not capture all external effects.
Compiler, cache and fusion candidatesNo mandatory enablement release; each must pass real-workload benefit, compatibility and resource budgets.May remain disabled indefinitely; implementation or correctness alone does not establish performance admission.

Release directions are not calendar commitments. In 1.7.1, HHY_CONCURRENCY_EXPERIMENTS=1 already enables the seven experimental APIs. Stable API availability and default optimization are separate decisions; missed gates delay admission without lowering thresholds.

Read current atomicity and concurrency guarantees
Separate supported behavior, experiments and future plans
/en/learn/concurrency-scopes

19.5Evolution principles

PrincipleConstraint
Freeze semantics firstStabilize Pipe, Value, Stream, Error, and cancellation semantics before broadening the ecosystem surface
Usable and measurable before fastEvery capability needs deterministic errors, resource bounds, and cross-platform tests before optimization
Protocol firstIntegrate third-party capability through the Process Extension Protocol instead of inventing a second language model
ABI only when justifiedEvaluate a Native ABI only after Runtime stabilization and measured need; choosing not to publish one is a valid result

Review the roadmap once per quarter. Only unfrozen releases may move; scheduling changes must not weaken published semantics, compatibility commitments, or migration paths.

19.6Explicit non-commitments

  • No second Pipe, Stream, or Error model merely to justify a release number.
  • No public exposure of internal Runtime C structures without a compatibility strategy.
  • No use of recommended windows as a reason to skip testing, security, or cross-platform validation.
  • No simultaneous rush into a remote registry, Native ABI, and multiple official extensions before stage gates pass.

Tooling · 20

Editor Language Support

Download and install HHY language packages for VS Code and Sublime Text.

20.1HHY 1.7.1 implementation and compatibility

1.7.1 passes local tests, four-platform CI and release validation. Language specification 1.0 remains frozen. Official sample, HTML and macOS/Linux Database 1.0.0 extensions are verified compatible. VS Code 0.2.0 and Sublime 0.1.0 retain independent versions; syntax and editor contract checks pass.

HIR, six optimization passes, integer MIR, type-feedback guards/deopt and local List scalar replacement are implemented. Explicitly enable HHY_COMPILER=ir, HHY_FEEDBACK_SPECIALIZATION=1 and HHY_SCALAR_REPLACEMENT=1; direct Bytecode remains default. Real workloads have not met default-enablement benefit gates. Scalar replacement retains original GC/quota reservations.

Optimization switches, verification and supported scope
v1.7.1 implementation
/docs/COMPILER_IR.md

1.7.1 release notes
Released capabilities and limits
/docs/RELEASE_NOTES_1.7.1.md

20.2Download language packages

Packages provide .hhy highlighting, brackets, indentation and snippets. VS Code also uses the hhy CLI for diagnostics, formatting, definitions, hover and completion; install HHY Runtime first. Plugin versions are independent of the Runtime.

Download editor packages locally
Installable VSIX and sublime-package files with SHA-256 checksums.
/en/downloads#editors

20.3Install in VS Code

sh
code --install-extension ./hhy-language-support-0.2.0.vsix

Run from your download directory, or choose Install from VSIX in the VS Code Extensions menu. Then open a .hhy file.

20.4Install in Sublime Text

Copy the downloaded HHY-0.1.0.sublime-package into Sublime Text's Installed Packages directory, then open a .hhy file.

Language Reports · 21

HHY Language Status Report · 2026-09-14

Published status of HHY semantics, Runtime, performance, and engineering quality with reproducible CI measurements.

21.11.7.1 controlled concurrency and atomicity

This release fixes resource lifetimes and delivers seven experimental APIs, disabled by default. Review state ownership, cooperative file locks, strict transactions and bounded-task guarantees before enabling them.

Read the complete guide and examples
Includes the guarantee matrix, failure handling and actual validation evidence
/en/learn/concurrency-scopes

21.2HHY 1.7.1 implementation and compatibility

1.7.1 passes local tests, four-platform CI and release validation. Language specification 1.0 remains frozen. Official sample, HTML and macOS/Linux Database 1.0.0 extensions are verified compatible. VS Code 0.2.0 and Sublime 0.1.0 retain independent versions; syntax and editor contract checks pass.

HIR, six optimization passes, integer MIR, type-feedback guards/deopt and local List scalar replacement are implemented. Explicitly enable HHY_COMPILER=ir, HHY_FEEDBACK_SPECIALIZATION=1 and HHY_SCALAR_REPLACEMENT=1; direct Bytecode remains default. Real workloads have not met default-enablement benefit gates. Scalar replacement retains original GC/quota reservations.

Optimization switches, verification and supported scope
v1.7.1 implementation
/docs/COMPILER_IR.md

1.7.1 release notes
Released capabilities and limits
/docs/RELEASE_NOTES_1.7.1.md

21.31.7.0 measured results and release evidence

One local macOS arm64 run on 2026-09-08 with HHY 1.7.0 and default compiler settings. The raw profiler output follows. Only four CPU samples were collected; short-run timings and hotspot percentages vary and do not establish a speedup.

$ hhy profile --engine bytecode examples/09-profile-algorithms.hhy -- fibonacci 20
HHY profile: examples/09-profile-algorithms.hhy

Summary
  Engine           bytecode
  Wall time        0.013 s
  CPU time         0.008 s
  CPU utilization  58.8%
  CPU samples      4
  Heap peak        1.8 MiB
  Heap after GC    92.0 KiB
  Allocated        1.7 MiB
  Allocations      33040

CPU hotspots
  CPU%    Samples      Calls  Function
   75.0%        3      21891  fibonacci  examples/09-profile-algorithms.hhy:5:1
   25.0%        1          1  print  examples/09-profile-algorithms.hhy:18:6
    0.0%        0          1  <bytecode-top-level>  examples/09-profile-algorithms.hhy:1:1
    0.0%        0          1  length  examples/09-profile-algorithms.hhy:12:10
    0.0%        0          1  to_int  examples/09-profile-algorithms.hhy:17:19
  Note: fewer than 10 CPU samples; use a larger workload for stable results.

Allocation hotspots
  Bytes          Objects  Function
  1.7 MiB            32856  fibonacci  examples/09-profile-algorithms.hhy:5:1
  11.6 KiB              184  <bytecode-top-level>  examples/09-profile-algorithms.hhy:1:1

fibonacci 6765
PlatformInteger MIR / baselineLocal List / baseline
linux-arm640.7624×0.7495×
linux-x86_640.7067×0.6738×
macos-arm640.8392×0.8553×
windows-x86_640.8478×0.8326×
Four-platform results come from release commit 8fb0b8f, CI 34197497786, with 15 interleaved pairs each. Both synthetic workloads pass their budgets; real core/json/closure workloads miss the 5% benefit gate, so optimizations stay opt-in. Scalar replacement retains GC/quota reservations. Older measurements below retain their historical scope.

View 1.7.0 CI and raw performance artifacts
8fb0b8f · 2026-09-08
https://github.com/hh696-wq/hhy-vm/actions/runs/34197497786

21.4Release summary

As of 2026-09-14, HHY v1.7.1 delivers Database 1.0.0 and scoped resources alongside the complete Web Runtime: embedding, HTTP/1.1, Router, middleware, uploads, streaming, SSE, multiple workers, and observability. v1.4.0–v1.4.2 are capability milestones shipped together in v1.4.3. Bytecode remains default; AST remains the semantic oracle and explicit fallback.
DimensionQuestionCurrent conclusion
Language baselineAre core semantics stable?Pipe, Value, Stream, Error, and core callable contracts are frozen
Runtime healthAre resource, memory, and cancellation boundaries reliable?Resource limits, GC stress, sanitizers, fuzzing, and explicit ownership governance are present
PerformanceIs performance measurable and controlled?Fixed workloads, five-sample medians, machine-readable evidence, and blocking budgets are established
Engineering governanceAre changes auditable?Four-platform CI, layered gates, version consistency, and release evidence form a closed loop

21.5Data at a glance

SignalResultEvidence basis
Current formal releasev1.7.1Four platform archives, per-asset SHA-256, and SHA256SUMS
Execution enginesBytecode default / AST fallbackFull dual-engine suite and machine-readable decision
Core callables128Runtime Callable Contract Registry
Continuous-verification platforms4macOS arm64, Linux arm64, Linux x86_64, Windows x86_64
v1.3.10 CI engine baselineHistorical measurement passed1M CPU 0.3695; short task 1.0088; sustained JSON 1.0207
v1.3.10 Profiler overhead1.0269× / +2.786 msHistorical nine samples; limits 1.35× and 12 ms
Bytecode cacheNot admittedFive workloads × 21 samples; compile+verify is not a major cost
Complete practical projects6AST/Bytecode end-to-end acceptance with stable exit status

21.6v1.5.0 / Database 1.0.0 release acceptance

Released 2026-09-07. Dual-database/engine, TLS, scope cleanup, cancellation, CMS database lifecycle and cross-platform release gates pass. Local million-row streaming and five-minute-per-backend sustained reports are available. RDS and 24-hour soak validation remain pending.

Read DB 1.0.0 acceptance
HHY 1.5.0 · Database 1.0.0
/docs/DATABASE_1.0.0_ACCEPTANCE.md

Read HHY 1.5.0 release notes
HHY 1.5.0 · Database 1.0.0
/docs/RELEASE_NOTES_1.5.0.md

21.7v1.4.3 Web Runtime and release verification

CheckResultMeasurement scope
Embedded Runtime100,000 repeated callsOne loaded context with AST/Bytecode differential output checks
HTTP stress verification1,000,000 requests · zero failuresLoopback on the release-validation host, 16 concurrent clients
HTTP duration and throughput131.439 seconds · 7,608.1 requests/secondHost-specific short-request workload, not a public-network or business-service throughput claim
HTTP regressionEnd-to-end coverageRouter, Query, 404/405/413/500, recovery, middleware, static caching, upload cleanup, CORS, signed cookies, and gzip
Streaming and operationsEnd-to-end coverageStream, SSE, Range, metrics, health, multiple workers, and development reload
This section cites v1.4.3 release-validation records, not a new benchmark run on 2026-09-04. CPU, Profiler, cache, and six-runtime comparisons below retain their original v1.3.10 versions and methods.

Read the v1.4.3 release-validation record
Site-hosted release notes with test scale, results, and capability boundaries.
/docs/RELEASE_NOTES_1.4.3.md

21.8Current capability boundaries

AreaCurrent boundary
HTTP servingRuntime provides HTTP/1.1; Caddy, Nginx, or a load balancer handles TLS and HTTP/2
WebSocketOutside the v1.4 capability boundary
Execution enginesBytecode default with AST fallback; no JIT, process/disk Bytecode cache, or unverified external Bytecode loading
Native integrationC embedding uses opaque handles and a JSON ABI; it does not expose Runtime internals or a third-party Native Extension ABI

21.9Overall baseline and compatibility

BaselineStable commitmentVerification
Language semanticsNo second Pipe, Stream, or Error modelSpecification examples, Parser/Checker fixtures, and valid-program regression
Callable contractsNames, arity, effect, lazy, cancellable, and threading metadata are machine-readableContract Registry JSON and current callable-contract consistency checks
DiagnosticsCLI text and JSON/LSP share the Core checking pathDiagnostic schema and LSP protocol tests
Extension boundaryThird-party capabilities prefer the Process Extension ProtocolManifest integrity, Protocol 1, and official-extension acceptance
C embeddingPublic opaque Application/Context handles and JSON ABI; Runtime internals remain privateOne-time loading, repeated calls, request isolation, and AST/Bytecode differential checks

The current formal baseline is v1.7.1. Bytecode is default; the AST evaluator remains the semantic oracle and is selectable through --engine ast or HHY_ENGINE=ast. Compiler-produced Stream Kernels must pass their independent Verifier; dynamic and unknown shapes fall back losslessly to general Bytecode.

21.10v1.2.2 release and extension status

CapabilityCurrent stateAcceptance result
Extension distributionEd25519-signed Registry and deterministic resolutionTampering, unknown sources, and dependency conflicts fail closed
Reproducible environmentLockfile and content-addressed offline cacheThe same lock yields the same graph and rebuilds offline
Safe changeTransactional install, upgrade, and explicit rollbackFailed upgrades preserve the old environment
HTML 0.2.0Lexbor, CSS selectors, and single-parse multi-field projectionMalformed HTML, hard limits, truncation, and structured errors pass on four platforms
Protocol decisionRetain the bounded synchronous batch APIHTML retains synchronous batches; DB 1.0 uses scoped handles, cancellation and host cursor Streams; generic Stream credit remains unavailable
The v1.2.2 release contains macOS arm64, Linux x86_64, Linux arm64, and Windows x86_64 archives, per-package SHA-256 files, and a combined SHA256SUMS. The HTML extension remains effect = none and performs no independent file, network, or subprocess access.

Open the HHY Language v1.2.2 release
Download all four platform archives and checksums and read the release notes.
https://github.com/hh696-wq/hhy-vm/releases/tag/v1.2.2

21.11v1.3.7–v1.3.10 Bytecode hardening

VersionCore deliveryVerified conclusion
v1.3.7Named specialization metadata, unified stack/error rules, fallback reasonsNo magic kinds; three-path differential and metamorphic gates pass
v1.3.8Compiler-produced versioned Stream Kernel IRRuntime does not inspect AST shapes; independent verification; safe dynamic fallback
v1.3.9Shared optimization decisions for normal and profiled executionKernel/opcode, cancellation, CPU/Heap attribution, and machine reports remain consistent
v1.3.10Performance-triggered cache governanceEvidence did not admit a cache; no process/disk cache; unverified external Bytecode rejected
Each version completed implementation, Release/Debug tests, sanitizers, fuzzing, measured performance, four-platform Actions, formal Release, and Homebrew Formula verification before the next version began.

Open the HHY Language v1.7.1 release
Four platform archives, per-asset SHA-256, SHA256SUMS, and Web Runtime release notes.
https://github.com/hh696-wq/hhy-vm/releases/tag/v1.7.1

21.12Historical performance baseline · v1.3.10

Final v1.3.10 CI evidence comes from commit 4ddc8c3 on GitHub Actions Ubuntu 24.04: schema-2 paired/interleaved engine benchmarks plus independent Profiler and cache-decision artifacts. Ratios are Bytecode/AST wall time; lower than one means Bytecode is faster.

GateMeasuredLimitResult
1M CPU0.3695×0.90×Pass
Short task1.0088×1.25×Pass
Sustained JSON/I/O1.0207×1.10×Pass
Profiler overhead1.0269× / +2.786 ms1.35× / +12 msPass
Cache-admission workloadCompile+verify medianCold-run medianShare
Hello0.0097 ms5.860 ms0.1649%
Advanced Flow0.0269 ms5.916 ms0.4546%
Stdlib0.0286 ms7.003 ms0.4086%
Sustained JSON0.0089 ms33.518 ms0.0266%
Core Flow 1M0.0105 ms166.285 ms0.0063%
The joint cache threshold is compile+verify ≥ 1 ms and ≥ 20% of cold-run wall time. Every workload remains far below it. Even assuming zero cache-read cost, reproducible benefit is absent, so v1.3.10 implements no process or disk cache and continues to reject unverified external Bytecode.

21.13v1.3.10 six-runtime same-machine rerun

Rerun on 2026-09-01 on macOS 26.6.2 arm64 with HHY 1.3.10, PHP 8.5.10, Go 1.27.0, Python 3.14.7, Lua 5.5.1, and OpenJDK 26.0.2.1. The fixed task maps, filters, stable-distincts, materializes, and counts one million integers; all six implementations validate output 333334. After two warmups, two independent rounds use seven deterministically shuffled and interleaved fresh processes per runtime. Wall time includes process startup; Go and Java are precompiled and compile time is excluded.

ImplementationVersionRound 1 medianRound 2 medianCombined range
Go1.27.07.995 ms7.969 ms7.622–15.325 ms
Lua5.5.118.368 ms17.957 ms17.582–19.822 ms
PHP8.5.1043.174 ms43.549 ms42.588–49.033 ms
JavaOpenJDK 26.0.2.149.394 ms48.153 ms46.995–53.066 ms
HHY Bytecode1.3.1055.297 ms53.404 ms51.027–78.702 ms
Python3.14.781.747 ms86.459 ms79.993–87.774 ms
ComparisonRound 1Round 2Interpretation
HHY / PHP1.28×1.23×HHY uses about 23%–28% more wall time on this task
HHY / Java1.12×1.11×Java is slightly faster including fresh-JVM startup
HHY / Python0.68×0.62×HHY takes less wall time on this task
HHY / Lua3.01×2.97×Lua is faster on this integer loop
HHY / Go6.92×6.70×Precompiled Go remains substantially faster
This is one CPU/materialization workload, not a general language ranking. Each implementation uses an idiomatic loop and distinct container; Java uses HashSet/ArrayList and includes fresh-JVM startup. All 84 timed samples, versions, orders, and sources are retained locally under performance-analysis/2026-09-01-v1.3.10-language-comparison/, which project policy excludes from GitHub.

21.14Governance conclusion and watch list

  • Overall status: v1.7.0 / DB 1.0.0 passed local, CI and cross-platform release gates; real RDS and 24-hour soak validation remain pending.
  • Web conclusion: embedding, HTTP, Router, middleware, uploads, Stream/SSE, multi-worker serving, and observability ship together.
  • Engine policy: Bytecode is default; AST remains permanently available as the semantic oracle, differential-test engine, and --engine ast emergency fallback.
  • Performance conclusion: one million Web short requests completed with zero failures; existing CPU, short-task, sustained-JSON, and Profiler gates continue to pass.
  • Cache conclusion: compile+verify is not a major cold-run cost; no cache is introduced until new data triggers review.
  • Update rule: synchronize this report whenever the release baseline, measurement method, engine/cache decision, or overall risk conclusion changes.

Open v1.3.11 continuous-verification evidence
Four-platform builds, sanitizers, fuzzing, performance gates, and practical-project acceptance.
https://github.com/hh696-wq/hhy-vm/actions/runs/33581452558

HHYHHY Language

A flow-first scripting language for system automation.

© 2026 HHY Language contributors
LearnQuick StartComplete ManualCLI Reference
ProjectAbout HHYDownloadsHHY CMSRelated ArticlesGitHubSpecApache 2.0
Contact hhylang.dev huiyang.hou@qq.comhouhuiyang.com