Named Concurrent Regions in Sigil
Sigil uses explicit named concurrent regions for batch concurrency.
The language keeps one promise-shaped runtime model, but widening work is explicit, bounded, and policy-driven.
The Problem
Broad implicit overlap sounds attractive until real programs need actual engineering controls:
- concurrency width
- rate windows
- jitter
- selective stop on systemic failures
- stable ordered results
Those are not properties of map. They are properties of an execution region.
The Surface
Sigil has one canonical concurrency surface:
λisSystemic(err:String)=>Bool=err="NETWORK"
λprocessUrl(url:String)=>!Timer Result[Int,String]={
l _=(§time.sleepMs(0):Unit);
Ok(#url)
}
λrun(urls:[String])=>!Timer [ConcurrentOutcome[Int,String]]=concurrent urlAudit@5:{jitterMs:Some({max:25,min:1}),stopOn:isSystemic,windowMs:Some(1000)}{
spawnEach urls processUrl
}
Important points:
- the region is named
- width is required after
@ - policy is optional and exact when present
- policy fields are alphabetical
- the body is spawn-only
Current policy fields are:
jitterMsstopOnwindowMs
Minimal form:
λprocessUrl(url:String)=>!Timer Result[Int,String]={
l _=(§time.sleepMs(0):Unit);
Ok(#url)
}
λrun(urls:[String])=>!Timer [ConcurrentOutcome[Int,String]]=concurrent urlAudit@5{
spawnEach urls processUrl
}
Why It Is a Region
The policy belongs to the region because the work inside it is not always one operator application.
A real batch often mixes:
- list traversal
- HTTP calls
- file writes
- nested bounded work
Attaching rate limits and jitter to map itself would be the wrong abstraction. Those concerns belong to the execution boundary that owns the batch.
Result Shape
Regions return one ordered list of outcomes:
t ConcurrentOutcome[T,E]=Aborted()|Failure(E)|Success(T)
That gives the caller one deterministic post-batch value:
Success(value)when a child returnsOk(value)Failure(error)when a child returnsErr(error)Aborted()when work was stopped before completion
Order is stable:
spawnorder for explicit child spawns- input order for
spawnEach
Error Policy
Sigil does not hard-code one default stop policy for all programs.
Instead, the region takes an explicit predicate:
λshouldStop(err:String)=>Bool=err="NETWORK"
That means a batch can keep going through local failures while still stopping on systemic ones.
For example:
- an HTTP
404may be modeled as an ordinary successful response - a transport failure may be modeled as
Err(HttpError)and triggerstopOn
This is the right split. The child computation classifies the domain result, and the region decides whether that failure should stop new starts.
List Operators Stay Canonical
This change does not introduce a second family of map helpers.
Sigil still keeps:
mapfor pure projectionfilterfor pure filteringreduce ... from ...for ordered reduction
Those operators are canonical value transforms, not the concurrency surface.
Runtime Model
The runtime keeps one clear story:
- normal code is promise-shaped, but not silently widened
- concurrent batching happens only inside
concurrent name@width{...}
Why This Is Better
It gives Sigil one explicit place for:
- bounded concurrency
- per-window start limits
- jitter
- stop behavior
- ordered outcomes
And it removes a lot of hidden behavior from ordinary expressions.
That is a better fit for Sigil's goals:
- one canonical surface
- visible cost shape
- deterministic result ordering
- less room for accidental fanout in generated code