Skip to content

feat(stdlib): async sequencing combinators + Async effect (Refs echidna#62)#471

Open
hyperpolymath wants to merge 1 commit into
mainfrom
feat/stdlib-async-echidna62
Open

feat(stdlib): async sequencing combinators + Async effect (Refs echidna#62)#471
hyperpolymath wants to merge 1 commit into
mainfrom
feat/stdlib-async-echidna62

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Single-commit stdlib addition: async module with sequencing combinators + declares the Async effect. Part of the affinescript stdlib roadmap (#412); refs echidna#62.

Recovered from local-only WIP branch during 2026-05-30 estate housekeeping pass.

…fs echidna#62)

Adds stdlib/future.affine and declares `effect Async;` in
stdlib/effects.affine — the async primitive for the
ReScript->AffineScript migration (echidna#62; Client.res's
`promise<result<T, string>>` chains via Promise.then/catch/resolve).

Model (thin combinators over Result + effect; user-chosen approach):
on the Deno-ESM target the compiler emits native JS async/await and
host promises cross as the Thenable extern ABI (#103) — suspension is
at the extern boundary (echidna#61 Http), so the source needs no
promise monad. An async value is its settled `Result<T, String>`
(Client.res's `result<_, string>`), carried by an `/{Async}`-effect
function. future.affine is the value-level half: a 1:1 map of the
ReScript promise chain onto that Result.

No wrapper type by necessity, not just preference: a user-defined
generic `Async<T>` (ADT or alias) is unusable in signatures — the
typechecker raises "Too many arguments for kind"; only prelude's
multi-constructor `Option`/`Result` are sound generic carriers. This
is a real AffineScript compiler limitation (single-constructor /
aliased generic types) — recorded for a follow-up affinescript issue.
`Result<T, String>` is also exactly Client.res's existing shape.

Surface: resolve (Promise.resolve) / reject / then (Promise.then,
async step) / map_ok (then with pure mapper) / recover (Promise.catch)
/ map_err. effects.affine now declares `effect Async;` (the #196 v1
registry already reserved the name; Vscode.affine already used
`/ Async` — this closes the stdlib coherence gap).

Verified: #136 stdlib-wide AOT smoke gate drives the new file +
modified effects.affine through resolve -> typecheck -> borrow ->
Deno-ESM codegen; `dune runtest` green incl. new "AOT future.affine"
case and unchanged "AOT effects.affine". Regression-protected on merge.

echidna#62 stays open as the consumer-side tracker per its protocol
(closes when the Client.res port lands, not here). Independent of
echidna#63/#64 (no code import) — independent PR off main. Next: #61
http.affine (Deno fetch extern -> the /{Async} boundary; where the
String->Json parse bridge lands).

Refs #128, hyperpolymath/echidna#62.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@hyperpolymath hyperpolymath enabled auto-merge (squash) May 30, 2026 19:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant