Skip to content
LogoLogo

Requirements

Normative model for capabilities and provider layers

Status: Locked for capability identity, context access, inference, and layer-based provision. Declaration encoding, lifecycle, and lowering are open.

Capability Identity

A capability MUST be represented by an abstract class extending Context from vibelang/context.

Two structurally identical context subclasses MUST remain different requirements unless the program defines an explicit identity relationship.

The abstract class MUST act as both the service contract and its nominal context key:

import { Context } from "vibelang/context"
 
abstract class Clock extends Context {
  abstract now(): number
}

This declaration uses ordinary class syntax. VibeLang MUST NOT add a separate capability declaration form.

Context Access

A function accesses a service with a library-shaped call:

function timestamp(message: string): Entry {
  const clock = Clock.context()
  return { message, recordedAt: clock.now() }
}

Capability.context() MUST be an inherited, compiler-recognized call on subclasses of Context. It MUST return the receiving capability's instance type and add that class's nominal identity to the enclosing function's inferred R row.

The call is intentionally library-shaped: it uses ordinary static method-call syntax and has ordinary runtime behavior after lowering, while the compiler gives it additional static meaning. The receiver MUST identify a Context subclass strongly enough for the compiler to record its nominal key.

The context row MUST be part of the function's static type even though it is not written with dedicated function-declaration syntax. Callers MUST NOT pass a context argument by hand.

Lookup MUST remain fully typed. The implementation MUST NOT expose an untyped service locator.

Inference

Calling a function with unsatisfied requirements MUST add those capabilities to the caller's R row. The source call MUST remain an ordinary call with only the function's declared value arguments.

Requirement inference MUST be transitive through ordinary calls. Duplicate nominal requirements MUST collapse.

Public declarations MUST preserve the inferred row. Its exact TypeScript declaration encoding is open; a phantom Context.Function<Fn, R> representation is a candidate.

Platform Requirements

Platform-sensitive functionality MUST use requirements. This includes filesystem, network, environment, clock, randomness, process, browser document, and comparable host facilities.

A capability MAY have multiple target implementations. For example, FileSystem MAY be implemented by Node, Bun, Deno, native, WASI, or a test provider.

Portability MUST be determined from the satisfied dependency closure, not merely the source module's import path.

Layer Algebra

Provider composition MUST be expressed by a library imported from vibelang/provider. The accepted design MUST NOT rely on a dedicated source-language provide {} statement.

A layer conceptually has the type:

Layer<Provides, Error, Requires>

Layer composition MUST track:

  • the nominal capabilities produced
  • failures during construction
  • capabilities required for construction

A layer MUST associate each capability's nominal context key with a compatible service instance. Layer APIs are library-shaped; the compiler recognizes their effect on R without introducing special provider grammar.

Satisfaction

Providing a layer to a computation MUST remove matching capabilities from the computation's unsatisfied requirement row. The provider library MUST establish the runtime scope in which Capability.context() resolves those instances.

Layer acquisition errors MUST enter the surrounding Result error union. Layer construction requirements MUST remain in the surrounding requirement row unless another layer satisfies them.

When the compiler knows the complete closure, an unsatisfied capability MUST be a compile error.

Scoping

Provided implementations MUST be scoped to the provided computation. Nested scopes MAY override implementations according to a future explicit precedence rule.

Resources acquired by a scoped layer MUST eventually be finalized. Acquisition order, memoization, sharing, override behavior, and disposal error policy are open.

Lowering

The TypeScript backend MAY lower requirements through hidden environment parameters, scoped ambient context, or another mechanism that preserves semantics and ordinary source-level call syntax.

Ambient runtime context is not a locked implementation choice. The lowering MUST preserve async scope, nesting, failure cleanup, and nominal lookup behavior.

TypeScript Requirement

TypeScript is a built-in capability representing runtime dependence on TypeScript/JavaScript-specific behavior.

It MUST propagate and be satisfiable or rejectable like other requirements. A native pin MUST reject any transitive TypeScript requirement.