On this page

Standard library plan

The standard library supports language semantics through ordinary .NET APIs, primarily implemented in C#. This plan explains responsibilities, public API boundaries, implementation strategies and edge cases. See the library reference for behavior and the API catalogue for declarations and typed source forms.

The standard library is part of the DirectSharp language experience: the objects and operations that make coordination, consumption, text and ordinary business applications work together. It will be implemented primarily in C# and exposed through normal .NET contracts. DirectSharp programs can also call existing .NET libraries directly.

Three kinds of support

Layer Purpose Examples
Public DirectSharp library Ordinary objects and methods that application code can use Gate, consumables, sequence helpers, Chars/Unicode, service providers
Compiler-selected support Preserve semantics that need state or coordination beyond one native expression Lazy state, structured concurrent outcomes, shared atomic coordination
Direct .NET APIs Reuse the platform and its established libraries Lists, dictionaries, LINQ, Task, streams, HTTP, ASP.NET Core, EF Core, Dapper, FluentValidation, logging

The root namespace is DirectSharp. It is automatically available unless the project disables that import. Subnamespaces remain explicit imports. DirectSharp.Runtime identifies the small support layer used by the C# backend; its private helper names do not become application vocabulary. Assembly/package layout is a separate design concern.

A DirectSharp keyword can require compiler work without requiring a public runtime class. Class kinds, for example, expand a class header; they do not need an intermediate CLR base class. Conversely, Gate is an ordinary library object even though language scopes manage its permits.

Contract status

Surface Established contract API detail still to settle
Consumable protocol Exact CLR acquisition/result declarations, IConcurrentConsumable marker and borrowed ownership Selected Consumables factories and ILiveConsumable; private native registration remains implementation work
Gate Construction forms, Acquire/AcquireAsync overloads, IDisposable permits, timeout/cancellation arbitration and ownership Public acquisition contract complete; concrete permit and timer choices are private implementation details
Sequences Selection, source absence, nullable IReadOnlyList Try results, error timing and traversal contracts Concrete containers and remaining additional helpers; strict/Try opt-in exports are selected
Text/Unicode Constant types, numeric widths, strict decoding, fifteen text extensions and selected context profiles Chars/Unicode and opt-in Text.TextExtensions selected; private implementation and package placement remain separate
Services Generic/keyed lookup signatures, null-key rejection, ServiceNotFoundException base/identity properties, collected IReadOnlyList plural results, lifetimes and ownership Two identity-bearing constructors selected; native integration and remaining error catalogue
Compiler support Observable source semantics Private helper ABI and direct lowering are implementation work, not missing application API votes

An established source call shape is not a promise that every final CLR overload has been selected. Examples below describe design requirements; the C# reference guide shows implementation patterns without claiming a shipped library.

Gate and structured permits

Gate must support exclusive and bounded access through the recorded construction forms Gate(), Gate(count) and Gate(count, timeout). The ordinary IDisposable contract lets the application own a shared Gate while enter and concurrent limits borrow it.

Case Required behavior
Count zero or negative ArgumentOutOfRangeException; no usable Gate
Zero acquisition timeout Try immediately; TimeoutException if capacity is unavailable
Timeout.InfiniteTimeSpan No acquisition deadline
Any other negative timeout ArgumentOutOfRangeException
Initially cancelled caller, live Gate and valid arguments Fail before taking even free capacity
Grant/caller/timeout race Commit one acquisition outcome atomically; restore provisional capacity if a cause wins
Failure to acquire Do not enter the body or release an unowned permit
CLR acquisition Acquire returns IDisposable; AcquireAsync returns ValueTask of IDisposable; default-timeout and explicit-TimeSpan overloads each accept optional CancellationToken
Returned permit Unique per successful acquisition; disposing releases at most once and never disposes the Gate
Successful entry Release exactly the acquired permit on every exit path
Nested entry Requires another permit; no reentrancy or fairness promise
Dispose with held/pending acquisitions InvalidOperationException; leave the Gate usable for settlement and later disposal
Successful disposal Idempotent; later acquisition throws ObjectDisposedException

A private SemaphoreSlim or equivalent is a possible C# building block. WaitAsync alone does not implement recorded cause precedence, grant commitment or guarded disposal. The design must coordinate admission and closure and handle large valid durations without narrowing them incorrectly. No raw Native property exposes the primitive. Acquisition timeouts remain distinct from whole-operation deadlines.

The selected Acquire/AcquireAsync signatures return IDisposable permits; no additional public lease type or unpaired Release method is selected. Both timeout overload forms use the same acquisition rules. The concrete permit can stay private. See the complete signatures and C# usage and concurrency rules.

Consumable interface and adapters

The accepted ordinary CLR operation is:

ValueTask<ConsumeResult<T>> AcquireAsync(CancellationToken cancellation = default);

It is the member of DirectSharp.IConsumable<T>. The readonly result type supplies HasItem, Item, FromItem(value) and Completed. A completed/default result has no item; its Item getter throws InvalidOperationException. Null, zero and false payloads remain items when their types permit them. An open source with no item waits; it does not fabricate completion.

ValueTask permits synchronous acquisition without changing the Task default of a DirectSharp async action. Each returned ValueTask follows normal .NET consumption rules. Native source errors/cancellation propagate. Adapters borrow their sources and do not implicitly dispose delivered items; existing source/item ownership remains unchanged. Loop completion, failure and early exit do not silently dispose sources/items or acknowledge, requeue or cancel producers.

Adapter family Acquisition/completion Important acceptance cases
Queue and Stack Dequeue/pop; empty ends a drain session FIFO/LIFO, destructive removal, permanent session completion, caller can reuse native collection
ConcurrentQueue/Stack/Bag Coordinated arrivals plus explicit producer completion and drain Temporary empty wait, wake-up/completion races, distinct acquired items, native order where defined, bag unordered
BlockingCollection Native blocking take until completed and drained Cancellation, borrowed disposal, worker settlement if bridging a blocking wait
ChannelReader Native async waiting/reading until completed and drained Buffered values before terminal failure, cancelled wait leaves channel live, no automatic producer cancellation
IDataReader/DbDataReader Read/ReadAsync within current result set DBNull field is data, ephemeral IDataRecord view, no hidden NextResult, borrowed cleanup, no direct concurrent raw-reader consume
TextReader/StreamReader Explicit line mode; native null is EOF Empty line is an item, native encoding/newline behavior, cancellation and read errors
Stream Explicit positive-size byte chunks; zero-byte read is EOF Nonseekable input, short positive read is data, independent chunk buffers, no implicit encoding

The compiler must recognize supported adapters and distinguish source capabilities. An arbitrary IConsumable implementation does not prove safe concurrent acquisition. Shared reader/stream cursors need acquisition serialization; independent delivered buffers alone do not make overlapping reads safe.

The selected ordinary CLR marker DirectSharp.IConcurrentConsumable<T> : IConsumable<T> adds no members. A custom implementation promises overlapping acquisition safety and acquired-item validity across subsequent acquisition. This is a library interface, not a keyword or proof of mutable-payload thread safety. Compiler-known adapters retain their established capability handling; the direct concurrent raw-reader prohibition stays in force.

Explicit producer failure in a completion-aware live wrapper closes producer admission, drains buffered items in native order, then propagates the original retained terminal failure instead of Completed. Later acquisitions retain that failure. This does not reinterpret a cancelled wait or consumer-body exception, alter native ChannelReader behavior or add acknowledgement/requeue. Consumables.FromConcurrentQueue/Stack/Bag and ILiveConsumable.Add/TryComplete are selected, with first-terminal-wins ordering and rejection of late admission.

Existing IEnumerator/IAsyncEnumerator cursors are candidates for explicit adapters with clear disposal/token contracts. PipeReader needs a buffer-lease/AdvanceTo design before adaptation. Broker acknowledgements and transactional delivery remain source-specific. Arrays, lists and arbitrary enumerable objects do not become consumable automatically. See the full adapter contract.

Sequence operations

These helpers need ordinary CLR-compatible APIs, with the bound method determining the contract. DirectSharp helper semantics must not replace an already-bound LINQ call or introduce an unrecorded import priority.

Family Work and result Cases to cover
First/Last Actual absence when no item exists Real Nullable result for value types; preserve reference identity and one existing nullable layer; required results check absence before fixed T publication
Inclusive Take(start, end) Deferred streaming selection requiring the whole zero-based interval Invalid numeric bounds fail at call; shortage fails during traversal, possibly after a prefix
Inclusive TryTake(start, end) Normalize negative positions, clip end, collect once at call Adjusted reversed/no selected interval returns nothing; result reuse never rereads source
Count Take/Skip/TakeLast Negative fails at call; deferred strict requested-count checking Strict zero valid, exact-length Skip present empty, shortage timing differs by operation
Count TryTake/TrySkip/TryTakeLast Normalize negative count; collect available selection at call No selected items returns nothing; TrySkip/tail need source exhaustion on general enumerables

Strict Take/Skip/TakeLast, including inclusive Take, reject a nothing source immediately with ArgumentNullException before numeric validation. Their Try counterparts return nothing immediately for a nothing source before normalization, including zero requests and invalid numeric arguments. Neither path enumerates a missing source. Present-source bounds, collection timing and source-error propagation remain unchanged; imported LINQ retains its own contracts.

Inclusive/count TryTake, TrySkip and TryTakeLast expose nullable IReadOnlyList<T> results. Successful selections supply Count/index access and retain their call-time collected membership/order without source re-enumeration. The interface is read-only, not deeply immutable or protected against implementation casts. Concrete container selection remains implementation work; strict deferred helpers are unchanged.

The C# design should use direct metadata only when equivalent behavior is established, such as known array Length and ordinary List Count/indexing. It must avoid an enumerating Count prepass, repeated source acquisition or ignoring a custom enumerator merely because Count exists. Preserve original reading/cleanup failures.

General tail/remainder operations may require storage and cannot finish on an unbounded source. Collected values do not deep-copy referenced application objects. Empty strict results remain present; ?? throw checks actual absence, not sequence length. See sequence contracts and reference patterns.

Text, numeric and formatting support

Surface Design requirement Boundary cases
Chars Named Char values; CRLF/NewLine are String NUL is a character, not absence; NewLine follows the platform
Unicode constants Named String values Preserve the documented scalar values
FromCodeUnit / CodeUnit Validate Long before narrowing; Char construction and Integer inverse Accept all 0–65535 units, including isolated surrogate units
FromCodePoint / CodePoint Scalar construction and exactly-one-scalar inverse Reject out-of-range/surrogate scalar values; reject empty/multiple/malformed inverse input
CodePoints Deferred fresh IEnumerable of Integer with immediate null validation Surrogate pairs combine; malformed UTF-16 throws when reached; early exit leaves suffix unread
Text transforms Invariant Upper/Lower and shared Unicode-category acronym/case segmentation Missing source fails; casing transforms strictly reject malformed UTF-16, while Upper/Lower retain native casing behavior
URI / escaped contents URI component profile; strict readable-Unicode JSON/ordinary-C# contents without outer quotes Canonical quote/control/line-separator escaping; no object serialization or HTML script embedding
Layout StringInfo text-element units; suffix-inclusive truncation and greedy word wrapping Preserve existing CRLF/CR/LF; normalize ASCII spaces/tabs; strict full validation even for unused/discarded text
Numeric classification Invariant grammar and Long/Decimal/finite-Double gates Overflow, malformed text, finite underflow, no locale-dependent guessing
Round/Truncate/Floor/Ceiling Twelve members of root Numbers; Integer digits; AwayFromZero default Native typed ranges/errors/special values; no extra overload or conversion priority; bare source-call binding remains separate
Interpolation and formatting Once-only ordered evaluation, empty text for nothing, current culture normally Source failures propagate; serialization is separate from display
Binary display Recorded hex/base64/text presentations Never read or consume an arbitrary Stream just to format it

Ordinary String storage remains UTF-16. A code unit, a Unicode scalar and a displayed grapheme are different concepts. Normal .NET parsing, Rune, formatting and encoding APIs are useful where their exact contracts match; helper code must supply the stricter language/library boundary where necessary. Root Chars/Unicode layout and the selected helper profiles/member matrix are fixed, including layout and JSON/ordinary-C# content escaping. The fifteen text extensions use opt-in DirectSharp.Text.TextExtensions with Integer layout inputs; the twelve numeric members use root DirectSharp.Numbers. HtmlEncode follows guarded WebUtility.HtmlEncode; XmlEncode buffers a checked, declaration-free XmlWriter element-text fragment with newline entitization. These profiles do not add general template rendering, extra overloads or static-import grammar.

Services and application composition

The library model includes ServiceProvider, ServiceScope, ServiceScopeFactory, ServiceRegistry and MainServiceProvider. It integrates with normal .NET DI and host registration rather than requiring a separate application container.

Capability Required behavior
Required GetService Missing singular registration throws DirectSharp.ServiceNotFoundException, derived from InvalidOperationException with read-only ServiceType/ServiceKey identity; activation/provider/root errors retain their separate categories
GetAllServices Resolve fully at the call; return IReadOnlyList, present empty for no contributors; reuse resolved instances
HasService Registration inspection without activation where supported; not permission for root activation
CreateScope / scope-factory Create Explicit bounded scope; no ambient current scope
Keys Type plus exact key; String keys are case-sensitive despite case-insensitive source names
Keyed API GetService, GetAllServices and HasService each have object-key overloads; null throws ArgumentNullException before lookup/activation; unkeyed calls use no-key forms
Lifetimes One singleton per root environment; scoped per active scope; transient per activation
Graph validation A singleton must not reach scoped state, including through a transient/collection
Native host registration Contribute to the existing registry without creating a second provider
Main provider Explicit appointment, one active provider, borrowed ownership and bounded clearing
Assisted construction Fresh caller-owned object from explicit scope; supplied needs override resolution

Needs and retrieved services are borrowed. Provider/scope cleanup owns created services. Replacing a replaceable need does not dispose the previous value. Metadata and constructor guards must preserve the effective inherited/class-kind dependency contract across DirectSharp/C# boundaries.

GetAllServices failure is observed at the call, without a successful partial result, rollback or ownership transfer for already-created services. Reusing a collected result does not activate new transient instances; separate calls still honor lifetimes. Scope/root restrictions and borrowed ownership remain in force, and the list does not freeze service state or extend a scope's lifetime.

The selected generic/keyed lookup signatures preserve exact typed key identity; statically invalid key declarations are diagnosed. ServiceNotFoundException exposes read-only ServiceType (Type) and ServiceKey (nullable Object, null for unkeyed lookup). Its two identity-bearing constructors are selected, without a formatter-based serialization contract. Native integration declarations and the remaining exception catalogue stay API work. Services and ownership explains the current model; the web API example shows ordinary Microsoft DI usage first.

Compiler support inventory

These responsibilities can use generated locals, CLR constructs or private support helpers. The language does not require a public class for each row.

Language behavior Compiler work Potential shared support
Case-insensitive names Normalize/bind deterministically, diagnose imported case collisions No public case-repair wrapper needed
Defaults and absence Select effective defaults while preserving actual stored absence Type/context default support where direct C# is insufficient
Class kinds and needs Expand effective class header and dependency metadata Dependency validation/activation integration
Contracts and old_value_of Capture entry expressions once in source order; run ensure only after successful cleanup Context/error helpers only if needed
try/try_get and LastAttempt Lexical attempt slot initialized before first attempt; preserve catch/lifetime behavior Attempt record representation and diagnostics
Lazy values Transparent forcing, assign-once binding, nonforcing state inspection Exactly-once publication, cached result/error and recursion detection
Async actions Preserve Task ABI/imported ValueTask, explicit Cancellation context and token binding Context/cancellation support when direct calls are insufficient
Concurrent scopes Own producers, schedule/limit/join, preserve task identity and source-order publication Outcome slots, first-cause arbitration and exception preservation
Inline run bodies Lower branch-local returns and explicit awaits to ordinary callables; settle cleanup and descendants before completion Reuse concurrent producer support; no new public task wrapper
Forgotten operations Warn on locally detectable abandonment of captured awaitables Compiler flow analysis; no runtime auto-await or exception swallowing
Swap and atomic regions Capture writable storage/accessors once and select compatible stable coordination Generic exchange/accessor helpers; no rollback
Events Retain compatible handler values; rewire tracked source paths and detach at applicable lifetime boundaries Ordinary CLR delegates/events; no public subscription runtime or dispatcher required
Resources and authored disposal Emit reverse cleanup and ordinary IDisposable/IAsyncDisposable plumbing Guards only where required; no hidden ownership of dependencies
Pattern/regex captures Bind eligible captures and their types without leaking dynamic captures Matching/formatting helpers where native contracts need adaptation
Metadata and source maps Preserve CLR signatures, attributes, descriptions and source-correlated diagnostics Stable compiler/runtime contract encoding

For example, Task.WhenAll supplies a useful join but not the complete first/any failure/publication contract. System.Lazy supplies useful caching; the lazy-value reference supplies a private state/publication strategy for nonforcing observations, cached outcomes, recursive-force waits and transparent source use. A new object lock per exchange does not coordinate accesses to the same shared state. The event reference separately shows ordinary delegate storage and source rewiring without a new event runtime. These boundaries are explained in the C# reference implementation guide.

Task.Run, ConfigureAwait, framework UI dispatchers, channels and observable/data-processing libraries remain ordinary .NET APIs. Applications can use them directly; optional feature adapters are useful only when they improve a particular library's ergonomics. Inline run does not introduce a separate observable protocol, scheduler API or standard-library implementation requirement.

Completion before implementation

The documentation phase completes the API catalogue and the cases each operation must satisfy. For each surface, record parameter/result types, null/empty/default distinctions, evaluation order, deferred work, exceptions, cancellation, ownership, concurrency and C# interop. Keep unselected declarations visibly separate from accepted signatures.

Reference C# patterns explain evaluation order, resource ownership and coordination. The language and library contracts define the required observable behavior.

A later implementation phase can turn the finished contracts into library projects and conformance tests. The roadmap connects these responsibilities to the broader application examples.

Compiler support and public API boundary

The runtime supports semantics that require more than a direct CLR expression: for example lazy publication/state, necessary context/default handling and contract failures. Compiler-selected helper names are implementation details, not extra source-language APIs. Public application contracts belong in the library reference; evaluation and lifetime patterns belong in the C# lowering guide.

The planned DirectSharp.Runtime layer does not select a final package layout, concrete private helper ABI or additional public factory signatures. Catalogue entries whose final declaration is still unselected remain described by their established behavior, rather than a guessed callable API. Native reference fixtures are pattern evidence, not the production implementation of this layer.

Catalogue completion boundaries

The declaration catalogue records feature discovery, operation effects, wrapper engine precedence and source/asset boundaries. These source contracts do not require another feature activation syntax, public compiler-plugin interface or template-engine registration grammar. Metadata encoding and the engine host ABI are private implementation details behind those contracts.

The explicit adapter factories/producer completion, opt-in sequence extensions, root Chars/Unicode layout, missing-service constructors, text transformation/layout/escaping profiles and numeric member matrix are selected in the API catalogue. Preserve their declared types and behavior during implementation. First/Last CLR optional-result binding and bare numeric-call lookup still need their concrete compiler account; a generic T? or an existing Math default cannot be substituted without checking the selected absence/midpoint rules. The C# absence account supplies concrete typed/private strategies and isolated native checks, while leaving the public First/Last exports open.

The selected TextExtensions signatures and distinct HTML/XML profiles complete those helper contracts. The C# text strategies and isolated fixture cover validation, slicing, word-packing and escaped contents under the selected Integer inputs. Encoding patterns and cases cover guarded native HTML and standalone XML text fragments, including carriage-return round trips and invalid XML input. Native service bridges must retain the host's provider and ownership, preserve exact keys, and distinguish known graph/introspection information from opaque external registration. The native DI integration cases map those obligations to host registration, inspection, activation, plural resolution and scope lifetime. Public signatures should follow those requirements rather than copying a private reference class.

Selected API implementation cases

The following cases define the behavioral boundaries an implementation must preserve:

Selected surface C# strategy Cases to verify
Text exports Fifteen String extensions in opt-in DirectSharp.Text.TextExtensions; Integer layout inputs Child import required; CLR signature/member lookup; reject unsupported Long narrowing and diagnose equally applicable extensions
HTML/XML profiles Guarded WebUtility.HtmlEncode; buffered checked XmlWriter element-text fragment with Entitize Null/empty/repeated entities; URI/HTML context separation; CR and supplementary-character round trips; invalid XML characters/UTF-16; attribute APIs remain separate
Numeric exports Root static DirectSharp.Numbers with the twelve typed members Qualified lookup, native typed ranges/defaults; resolve the recorded bare-call binding/collision boundary before compiler conformance
Borrowed factories Thin source-specific sessions; validate before constructing without reading Null source wins before chunk-size validation; construction performs zero reads/disposals
Queue/stack sessions Keep terminal session state after first empty observation New native items do not revive a completed adapter; a new session can see them
Channel/blocking factories Preserve native completion; serialize a single adapter's acquisition if needed Stable acquired items, native cancellation/faults, owned blocking bridge and no cross-adapter lock promise
Reader factories Static overload determines native IDataReader/DbDataReader acquisition path Current result set only; no NextResult/Dispose; raw concurrent use rejected
Live producer state Coordinate admission/native insertion/notification and terminal state; never suspend under the state lock Add versus completion race, exactly one terminal winner, late Add rejection, null failure checked even when already terminal
Live waiting Recheck under coordination and await a shared arrival/terminal signal No lost wakeup; cancelling one wait leaves other waiters/producers usable; no blanket per-item cancellation check
Live failure Capture retained cause after admission closes; drain before exposing terminal failure Same original failure after queued items, repeated failure observation, queue/stack/bag native order
Sequence exports Normal extension/static binding with int parameters Opt-in namespace, LINQ ambiguity, explicit qualified choice, unchanged bounds/timing
Text exports Fixed const fields plus runtime newline getter; preserve selected validation/iterator split CLR constant-context use, platform newline, Long bounds and fresh strict CodePoints
Missing-service constructors Identity-first initialization and ordinary Exception inner cause Null Type fails; null constructor key is valid unkeyed metadata; null message default; activation is not relabeled missing

These cases complement the existing native reference fixtures. Those fixtures predate the selected factory/live producer API; their passing history does not establish this implementation. The API catalogue supplies the exact selected declarations.