Version: 0.0.1 Date: 2026-07-06 Status: design draft
Hum is an intent-first systems programming language.
It is designed for code that needs the power of Rust and C++, the readability of Python, the explicitness of Zig, contract depth from verification languages, and compiler-generated context that humans and coding agents can trust.
The central rule:
Humans write intent.
The compiler enforces promises.
Agents fill, review, optimize, and explain from structured context.
See docs/LANGUAGE_REFERENCE.md for the traditional reference spine, docs/LANGUAGE_CONSTITUTION.md for the rules Hum must not violate, and docs/SECURITY_MODEL.md for the cybersecurity threat model.
- Readability beats cleverness.
- One obvious way is better than many clever ways.
- Effects are part of the interface.
- Memory safety is the default.
- Unsafe code must be visible, bounded, and justified.
- Allocation, blocking, networking, randomness, time, mutation, and IO are never hidden.
- Contracts should help the compiler, verifier, test generator, optimizer, reviewer, and agent.
- The compiler should generate semantic context instead of asking agents to infer it from raw text.
- Performance claims must be measurable.
- The language should stay small enough to teach.
- Do not clone C++.
- Do not require headers.
- Do not make source code plain English.
- Do not hide effects behind innocent-looking calls.
- Do not make macros the first escape hatch.
- Do not accept undefined behavior as normal systems programming.
- Do not depend on an AI model to make code correct.
Hum does not use C/C++ style headers. A .hum file is the source of truth.
The compiler produces derived artifacts:
source.hum human source
source.interface public typed interface
source.graph AST, call graph, ownership graph, effect graph
source.proof proof and test obligations
source.optimized backend IR and machine code
Imports are capability-aware:
use std.network
use std.time as clock
use std.random.secure
A task cannot use an imported capability unless its contract declares it.
Hum uses braces for stable machine parsing and newline-oriented statements for human readability.
Semicolons are not part of normal source.
task add(a: Int, b: Int) -> Int {
does:
return a + b
}
An app describes an executable program and its top-level capabilities.
app counter {
why:
count button presses and show the total
starts with:
run_counter
task run_counter -> Unit {
does:
return
}
}
starts with: names exactly one task directly nested in the app. It is an
executable entry declaration, not state initialization. App state
initialization remains undesigned.
For the current executable slice, app uses: is the maximum source-authority
budget. The only external capability spellings are stdout.write,
clock.replay, and files.read. Every task and caller repeats the exact
capabilities in its closed direct-call route; the app covers the start-task
closure. These declarations are not operator consent. Session Z adds the
exact one-run stdout.write operator grant and
stdout_write(text: Text) -> Result Unit, OutputError. The grant set defaults
empty, exact deny wins, output is immediate exact UTF-8 with no newline, and a
1 MiB rolling limit is checked before adapter access. --entry remains pure
and cannot carry one of these capabilities even when --allow is present. The
exact stdout_write name is reserved against user task declarations. Session
AA adds exact one-run clock.replay consent plus repeatable
--replay-tick <UInt> runner input, bounded to 1024 values. The exact
clock_replay_tick() -> Result UInt, ReplayClockError operation consumes that
sequence in order. Denial and exhaustion are typed failures; replay values do
not grant authority, and no host clock is read. Session AB reserves opaque
Path as a runner-only native identity for at most one structural app start
parameter. On Windows, one ordinary drive-letter-rooted argument is preserved
through args_os/OsString after lexical rejection of relative, traversal,
namespace, ADS, trailing-dot/space, empty-component, and normalized DOS-device
spellings. The value has no source construction, display, conversion,
comparison, storage, return, or path API. One exact native
--allow files.read=<path> payload may be parsed idempotently and exact
--deny files.read wins. Session AC may narrow either internal status from
locality_unclassified to threat-scoped fixed_local_v0 only when stable
drive/mapping observations, an empty storage-dependency query, a complete
nonempty extent list, and non-removable ATA/SATA/NVMe descriptors for every
backing disk all agree. Under its trusted-kernel/storage-driver/non-deceptive-
hypervisor boundary, this excludes observable mapped, network, fabric,
file-backed, virtual, removable, and unknown layers. Classification opens only
synthesized query-only volume/disk devices with desired access zero; it neither
performs nor authorizes candidate metadata, candidate open, or file reads.
Session AD adds exactly
files_read_text(path: Path) -> Result Text, FileReadError. Source authority
must cover files.read through the task/caller/app closure, and operator
consent must be one matching native files.read=<path> grant with deny-wins
precedence. Lexical revalidation and fixed_local_v0 complete before candidate
access. The reader rejects reparse components, directories, missing or
non-files, reads at most 1 MiB through one read-only open, and decodes strict
UTF-8. This is a Windows bootstrap adapter, not portable IO or a filesystem
sandbox.
A type describes data and its invariants.
type Session {
user: UserId
expires_at: Time
keeps:
expires_at is after created_at
}
A store describes data structure intent. The compiler chooses or verifies the
implementation strategy.
store sessions: map SessionId -> Session {
expects:
up to 20 million active sessions
optimizes:
lookup speed
memory density
collision resistance
protects:
session ids cannot be guessed
expired sessions cannot be reused
hides:
hash layout
bucket metadata
resize strategy
}
A task is Hum's function-like unit.
task name(input: Type) -> Output {
why:
uses:
changes:
needs:
ensures:
fails when:
does:
}
Tiny tasks may omit most blocks:
task square(x: Int) -> Int {
does:
return x * x
}
Critical tasks should declare deeper context:
task name(input: Type) -> Output {
why:
uses:
changes:
creates:
deletes:
needs:
assumes:
ensures:
keeps:
protects:
trusts:
watch for:
cost:
avoids:
tradeoffs:
allocates:
calls:
fails when:
optimizes:
tests:
benchmarks:
proves:
does:
}
A test is executable evidence for a contract, edge case, or behavior.
test add_item_rejects_empty_title {
covers:
add_item fails when title is empty
does:
expect add_item("") fails with TaskError.empty_title
}
Top-level tests may be unit tests, property tests, fuzz tests, regression tests,
or generated contract tests. Regression tests use test ... regression, not a
separate top-level keyword.
Human and agent-facing purpose. It should explain why the task exists.
Read dependencies and external capabilities.
Inside an executable app, external capability IDs form a closed vocabulary:
stdout.write, clock.replay, and files.read. Other lines remain ordinary
dependencies, while other dotted external-capability spellings reject. A
pinned declaration is an authority budget even before its operation ships, so
callers and the app must cover it transitively. Source declaration never grants
operator consent.
Examples:
uses:
user.id
clock.now
random.secure
sessions
Write permissions. If a task writes something not listed here, compilation fails.
New resources, records, files, handles, threads, tasks, or allocations.
Resources this task may remove, close, free, revoke, or invalidate.
Preconditions required before the task can run.
Facts this task depends on but may not be able to prove locally.
Postconditions the task must satisfy on success.
Invariants that must remain true during execution.
Security, privacy, memory-safety, or correctness properties being defended.
External authorities or unchecked assumptions.
Examples:
trusts:
random.secure is cryptographically strong
operating system enforces file permissions
Known edge cases and future hazards.
This is not just a comment. The compiler and tools should use it to generate tests, fuzz targets, review prompts, and diagnostics.
Expected time, space, allocation, IO, lock, or hardware cost.
Examples:
cost:
time: O(items)
space: O(1)
allocates: nothing
check: compile
In early milestones, cost: is emitted into the semantic graph. Later, the
compiler should compare it against loop structure, calls, allocation, profiling,
and benchmarks.
Implementation shapes this task should not use.
Examples:
avoids:
nested scan over users and sessions
allocation inside loop
network call inside loop
This is the compiler-grade form of a beginner saying what the code "dislikes."
Accepted engineering compromises.
Examples:
tradeoffs:
linear scan is acceptable because the list is small
dense storage is preferred over pointer stability
Measured performance requirements for named build or hardware profiles.
Examples:
benchmarks:
target: release-x64-baseline
input: 100_000 items
p95 time: under 5 ms
peak memory: under 10 MB
check: bench
Benchmarks are first-class, but hardware-dependent. They should fail benchmark or release profiles, not pretend to be pure type facts.
See docs/PERFORMANCE_CONTRACTS.md.
Memory behavior and allocation permissions.
Examples:
allocates:
nothing
allocates:
one session record
no unbounded temporary buffers
Important dependencies and allowed task calls. This supports review, impact analysis, sandboxing, and agent navigation.
Regression history for a test. This records the bug, issue, incident, or failure mode the test prevents from returning.
Examples:
regression:
found when title " " was accepted as nonempty
Test coverage intent. This names the task, branch, contract, risk, or diagnostic a test exists to exercise.
Examples:
covers:
add_item fails when title is empty
add_item watch for title may be only spaces
Explicit failure modes. Hum errors are typed and cannot silently disappear.
Optimization priorities. These should map to benchmark or profiling checks.
Required tests, generated test obligations, or fuzz cases. Use top-level test
blocks for executable tests.
Formal or semi-formal obligations.
Executable body.
Hum should eliminate most comments by giving important meaning a checked place to live.
Comments are still allowed, but critical claims should move into contract blocks:
why:
protects:
watch for:
needs:
ensures:
optimizes:
If a sentence affects correctness, security, performance, ownership, allocation, failure behavior, or review, it should eventually become compiler-visible.
The first passed-callable slice recognizes only task(UInt) -> UInt on one
receiver parameter, one same-file resolved task value, and one complete
transform(value) return expression. Its omitted latent row is accepted only
after shared analysis establishes a closed empty row and no nominal failure
root. The runtime value is a nonescaping definition handle, not a closure or
source-visible value. H1401 rejects unsupported forms and H1402 rejects exact
signature/failure-root mismatches before execution.
Hum should provide the constructs programmers expect, but with readable and checkable forms.
The precise executable subset is tracked in docs/FORMAL_CORE.md. Surface Hum may grow friendlier, but every executable phrase must lower into that smaller core or remain experimental.
Starter executable forms:
let name: Type = value
change name: Type = value
let alias = change owner.field
set name = value
if condition { ... } else { ... }
match value { ... }
while condition { ... }
loop { ... }
for each item in items { ... }
for index i in 0..count { ... }
return value
fail error
break
continue
Hum should not use C-style for (init; condition; step) loops.
Critical loops may carry nested intent:
while attempts < max attempts {
keeps:
attempts <= max attempts
changes:
attempts
does:
set attempts = attempts + 1
}
Loop keeps: clauses become invariants. Loop changes: clauses restrict local
mutation. Loop watch for: clauses should generate tests and review prompts.
See docs/CORE_LANGUAGE_SHAPE.md for the broader core construct plan.
Hum contracts should tell the compiler who is responsible when a promise fails.
Draft blame map:
needs: caller did not satisfy the precondition
ensures: task did not satisfy the postcondition
keeps: body or mutation broke an invariant
protects: security property was violated
trusts: external trust boundary was wrong or underspecified
changes: task mutated something it did not declare
allocates: task exceeded its memory policy
Every diagnostic should include the failed block, source span, blame target, and a machine-readable repair hint.
Hum is statically typed with local inference.
Primitive starter types:
Bool
Int
UInt
Float
Bytes
Text
Time
Duration
No value is nullable by default.
Optional values are explicit:
maybe User
Fallible values are explicit:
Result User, LoginError
Hum aims for Rust-level memory safety with more readable declarations.
Draft ownership modes:
owned Buffer
borrow Buffer
change Buffer
shared Buffer
Meaning:
owned T: this scope owns destruction or transfer.borrow T: read-only temporary access.change T: temporary mutation with exclusive access.shared T: thread-safe shared access through a checked type.
The compiler tracks lifetimes, aliasing, mutation, moves, and destruction.
The current implemented alias slice is much narrower than that target. In one
straight-line task body, let alias = change owner.field creates a writable
alias only when owner has visible mutation authority. Reading the alias reads
the current direct field and set alias = value writes through to it. The
lifetime ends after the last syntactic use. H0808 rejects an overlapping
direct read/write, owner-wide access, or second alias while live; a definitely
distinct direct field remains accepted. H0809 rejects branches/loops, escape,
passing, storage, borrow/change/consume wrapping, alias-to-alias binding,
nested/element places, rebinding, and shadowing an already-visible parameter,
local, or declared uses:/changes: permission.
This is not general aliasing, internal references, or flow-sensitive borrowing.
Unsafe code is allowed only inside visible unsafe boundaries.
unsafe task copy_bytes(from: Buffer, to: Buffer, count: Bytes) {
why:
move raw bytes without reading or writing outside either buffer
needs:
count <= from.length
count <= to.length
from and to do not overlap
proves:
no out of bounds read
no out of bounds write
does:
copy count bytes from from to to
}
Unsafe tasks must declare why:, needs:, proves:, and watch for:.
The full unsafe gate is docs/UNSAFE_POLICY.md.
Effects are part of task interfaces.
Starter effect set:
read
change
allocate
free
network
file
time
random
block
spawn
unsafe
foreign
Effects are inferred from contracts and body, then checked against the declared interface.
Errors are values, not hidden control flow.
task load_profile(id: UserId) -> Result Profile, LoadProfileError {
fails when:
profile is missing
storage is unavailable
does:
return profile from storage
}
Callers must handle failure explicitly.
The first checked propagation slice is deliberately narrow:
let value = try fallible_call()
let value = try fallible_call() or fail OuterError.context
The unwrapped form requires identical nominal caller and callee error roots.
The wrapper root must match the caller root and preserves the inner cause.
Known fallible calls without try reject in every currently executable
expression position, including nested calls and loop collections. Runtime
failure chains retain root origin and every recognized propagation/wrapping
site; they are exit-1 typed failures, not traps. This does not add recovery,
exceptions, first-class Result, checked variant membership, or general call
typing.
Concurrency must declare shared state and cancellation behavior.
task serve_requests(socket: Socket) {
uses:
network
sessions
changes:
sessions
watch for:
request may be cancelled
client may disconnect mid-write
shared session state must not race
does:
for each request from socket {
spawn handle request(request)
}
}
Draft concurrency features:
- Structured tasks.
- No detached work without
creates: background task. - Cancellation is explicit.
- Shared mutation requires checked synchronization.
- Lock ordering can be declared and verified.
Compile-time code must be sandboxed by capabilities.
build task generate_syscall_table() {
uses:
files.read
changes:
generated.syscalls
does:
read syscall spec
generate syscall table
}
Compile-time code cannot access network, system time, random, environment variables, or files unless declared.
The compiler should emit compact, queryable context:
hum graph
hum explain H0009
hum effects
hum risks
hum tests
hum agent docs
Agents should use compiler-generated graphs instead of guessing from raw text. The agent docs command should emit the exact grammar, diagnostic schema, examples, and repair workflow for the installed compiler version.
The standard library should be designed from a 2026 systems perspective:
- dense maps and sets
- region and arena allocators
- explicit async and IO
- safe FFI boundaries
- cryptography with misuse-resistant APIs
- SIMD and hardware feature detection
- parser and protocol primitives
- graph storage and compact indexes
- deterministic replay and tracing hooks
- property tests, fuzzing, and benchmarks as first-class citizens
See docs/STDLIB_STRATEGY.md for the standard library build plan.
- Should Hum use indentation significance inside blocks, or braces plus labels only?
- Should
does:be a controlled natural syntax or a smaller expression language first? - Should ownership use Rust-like references internally or region-based ownership first?
- How much proof should v0 attempt versus test/fuzz/contract checking?
- Should the first compiler lower to Rust, C, MLIR, or a custom interpreter IR?
- How should package names work when exact
humpackage names are unavailable in some registries? - Should standard library data structures expose chosen implementations or only intent contracts?
See docs/BACKEND_STRATEGY.md for the backend plan.
The first useful milestone is not a full compiler. It is a parser and semantic checker for task contracts.
Milestone 0 should:
- Parse
.humfiles. - Build an AST.
- Validate task and test block order and names.
- Emit a JSON semantic graph.
- Check that
does:only changes items declared inchanges:. - Generate a task interface summary.
- Generate test skeletons from
needs:,ensures:, andwatch for:. - Emit
cost:,avoids:,tradeoffs:, andbenchmarks:into the semantic graph. - Parse top-level
testblocks and connect them totests:obligations. - Preserve test kinds such as
property,fuzz, andregression.
See docs/ROADMAP.md for the build and teaching order.