Skip to content

stdlib has never been AOT-compiled as a coherent unit (angle-applied types + flat-namespace collisions; string.affine is the first symptom) #128

Description

@hyperpolymath

Summary

The AffineScript stdlib has never been compiled through the static
resolve → typecheck → codegen pipeline as a coherent unit.
It is
interpreter-era code (dynamically typed, flat global namespace) that the
AOT path has never exercised end-to-end. stdlib/string.affine is the
first file that surfaced this (originally reported here), but the two
root causes are stdlib-wide, not string-specific.

This issue is rescoped from "stdlib/string.affine does not compile" to
the actual underlying defect.

Reproduce (original symptom)

affinescript compile stdlib/string.affine -o /tmp/x.deno.js --deno-esm
# => affinescript: Resolution error  (fails at resolve, before typecheck/codegen)

Root causes (both stdlib-wide)

1. Angle-bracket applied types are the stdlib's surface, not a string.affine quirk

The static front-end accepts <...> for type parameters but
rejects it for applied types in value/return position
(fn char_at(...) -> Option<Char>). That asymmetry is the actual
defect. It is pervasive, not local:

file <…> usages
option.affine 36
result.affine 24
collections.affine 22
testing.affine 18
prelude.affine 15
io.affine 10
string.affine 5

Rewriting only string.affine to bracket form yields a file that still
cannot use option.affine / prelude.affine because those don't
resolve either — a single-file smoke check would pass while the stdlib
remains incoherent.

2. No module/import story; flat-namespace collisions

No stdlib file uses use / import / open. prelude.affine and
option.affine both define is_some / is_none / unwrap /
unwrap_or / map / filter / contains, with conflicting
signatures
(prelude.map(arr, f) vs option.map(f, opt)). Commit
b895374 ("seed Some/None/Ok/Err builtins so it resolves standalone")
is a band-aid that entrenches the interpreter-era global namespace
rather than resolving it; the original "add prelude/option imports or
qualify Option::Some" suggestion assumes import machinery the stdlib
has never been run through under AOT.

Decision required (resolve at source)

Pick the applied-type surface once and apply it uniformly. Per-file
rewrites are the worst of both options.

  • (Preferred) Accept Option<Char> in applied position in the
    resolver.
    Cheap, consistent with the <...> parameter syntax
    already accepted, makes the entire existing stdlib compile
    unchanged.
  • Bracket-only. Migrate all of stdlib/*.affine + docs + examples
    in one sweep and remove the angle alias so it cannot rot back.

Scope of work

  1. Decide + implement the applied-type surface uniformly (above).
  2. Resolve the duplicate-definition / namespace question: does the
    stdlib have modules, or stay flat-and-deduplicated? Remove the
    conflicting prelude vs option overlaps either way.
  3. Add a stdlib-wide AOT compile-smoke gate: every
    stdlib/*.affine through resolve → typecheck → codegen, plus at
    least one test that uses several stdlib modules together, so the
    AOT path can't silently rot again. (Not a single-file check.)

Impact / scope

Non-blocking and separate from #122. The Deno-ESM consumer migration
(ubicity#30 → ubicity#31/#33) deliberately does not depend on the stdlib
(self-contained extern fn primitives; ubicity green 44/44). This is
the separately-tracked structural loose end noted during the #122 work,
now scoped to its true root cause.

Refs #122, #123, #125, #127. Supersedes the original
string.affine-only framing of this issue.


Tracking (sub-issues)

This issue is now the tracking epic. Work items, with dependencies:

Suggested order: #131#132#133#135#136#137#138, with #134 doable anytime in parallel.


Downstream requirements (consumer-driven, from the ECHIDNA migration)

Folded in from the ReScript→AffineScript migration backlog. These are
requirements this epic must satisfy, not separate work streams — the
Client.res port (echidna [migration-roadmap.rescript-to-affinescript])
is blocked until the stdlib exposes these primitives. They extend the
stdlib surface the AOT pipeline (#135) must compile, beyond the existing
angle-bracket/namespace work:

echidna issue primitive needed maps to stdlib
hyperpolymath/echidna#61 Http/Fetch (GET/POST, RequestInit, Response ok/status/json) new stdlib/http.affine (no module today; effects.affine has no HTTP)
hyperpolymath/echidna#62 Async[T]/Promise[T] (pure/bind/catch or await) concrete Async[T] over effects.affine (currently no documented type)
hyperpolymath/echidna#63 Json (opaque type + Decode/Encode combinators, stringify) new stdlib/json.affine
hyperpolymath/echidna#64 Dict/Map (empty/get/insert/set) collections.affine (listed TODO; depends on #133/#135)

Implication for scope: #135's "every stdlib/*.affine compiles" must
include the new http.affine and json.affine plus a real
collections.affine, and #137's multi-module integration test should
exercise the Client.res-shaped path (Http + Async + Json + Dict together)
as the migration's validation target. The four echidna issues stay open as
the consumer-side trackers and close when the migration lands, not when this
epic does.

Refs hyperpolymath/echidna#61, hyperpolymath/echidna#62, hyperpolymath/echidna#63, hyperpolymath/echidna#64.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaves incorrectlymajorMajor issue — significant scope, broader impact than a feature/bug

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions