Nocter v0.44.0 Release Notes
Nocter v0.44.0 separates asynchronous producer declarations from future value types. async now states how a function or method executes, while future T names the move-only lazy computation value returned by that invocation. The compiler no longer guesses execution behavior from the outer shape of a result type.
This release intentionally changes source syntax. The previous async T type spelling is removed without a compatibility alias or fallback parser.
Explicit Asynchronous Producers
An asynchronous function or method places async before its callable kind. Its annotated result is the value produced by the body; calling it returns future Result.
async func fetch(url: Url): String! {
// The body produces String!.
}
let pending: future String! = fetch(url)
let response = await pending?
The execution choice is part of the callable declaration rather than a property inferred from its result. Interface requirements and their implementations must agree on immediate or asynchronous execution even when both invocation signatures would otherwise produce the same future type.
interface Source {
pub async method &self.read(): String!
}
instance FileSource {
impl Source
async method &self.read(): String! {
// ...
}
}
Structural Future Values
future T is an ordinary structural owning type. It may be stored, moved, passed to another call, or returned by an immediate function. Every future is move-only because one value owns starting, cancelling, and consuming its work.
Creating a future remains lazy. await consumes it, starts or resumes its producer, and returns T. Destroying unfinished work cancels it and releases initialized captures exactly once; destroying completed but unconsumed work destroys its stored output exactly once.
Type layering is explicit:
future String! // awaiting produces String!
(future String)! // an immediate failure or a future String
future future T // two deferred layers
Outcome propagation applies after awaiting. Therefore await pending? means (await pending)?. The opposite order is written explicitly as await (pending?) and requires an outer fallible value such as (future String)!.
Immediate Future Forwarding
Returning a future does not make a callable asynchronous. Generic and named forwarding functions remain immediate and merely transfer an existing computation:
func forward<T>(value: T): T {
move value
}
func forward_pending(value: future usize): future usize {
move value
}
An async func nested(): future usize deliberately creates a call of type future future usize; the compiler does not flatten it implicitly. Compiler-owned primitives may also construct future T immediately without being asynchronous Nocter bodies.
Ownership, Provenance, and Native Execution
Pending computations retain the invocation inputs needed by their bodies. Borrowed inputs constrain the future's lifetime, while a from clause continues to describe only the value produced by await. The two contracts are independent.
The existing suspension, cancellation, structured join, descriptor readiness, monotonic deadline, TCP, HTTP, TLS, and process-entry machinery now consumes the explicit callable execution fact and the structural future type. MIR, Machine, ARM64 lowering, and editor features do not inspect source text or repeat result-shape classification.
An asynchronous main remains a supported process entry. The generated adapter owns and drives its root future, consumes the declared body result, and applies the ordinary process exit policy.
Editor and Diagnostic Support
Formatting, hover, completion, signature help, navigation, semantic tokens, inlay hints, and missing-interface-method code actions use the same checked execution contract as compilation. Hover displays async func ...: T for a producer whose invocation type is future T; immediate functions returning futures remain displayed without async.
Invalid old type syntax, asynchronous primitives, asynchronous constructors or operators, await outside an asynchronous body, borrowed await operands, mismatched interface execution, and the current unsupported noalloc async combination receive source-backed diagnostics.
Compatibility and Non-goals
Migrate an old producer such as func fetch(): async String! to async func fetch(): String!. Migrate a field, parameter, alias, primitive result, or immediate function result such as async String! to future String!.
v0.44.0 does not add asynchronous closures, detached tasks, background execution, multiple executor threads, asynchronous DNS, a public polling interface, or a noblock guarantee. Existing _async API names are retained in this release; naming cleanup requires a separate public API decision.
The supported distribution remains a self-contained arm64-darwin compiler and standard library.
Release Qualification
Release-content commit 2409fb55d11eccb00d82f655128df024fdcbcda4 passed two independent complete compiler gates and two independent optimized package builds. The resulting archives and installed homes were identical. Fresh extraction passed the installed CLI, every public example, selected exact process contracts, native execution, framed LSP analysis, installed-home immutability, and compiler and standard-library tamper rejection.
The nocter-v0.44.0-arm64-darwin.tar.gz release is the exact retained qualified archive. It is 8,918,519 bytes with SHA-256 13b8fd6fdea3508cdab5be410581ecccee6a046f86313595631c31a1fad2a41e; publication reused this retained archive without rebuilding it.