diff --git a/crates/psrs-driver/src/tests/foldable.rs b/crates/psrs-driver/src/tests/foldable.rs new file mode 100644 index 00000000..c36570af --- /dev/null +++ b/crates/psrs-driver/src/tests/foldable.rs @@ -0,0 +1,56 @@ +//! The library's `Monoid` and `Foldable` classes. +//! +//! `foldMap` and `fold` are constrained by `Monoid`, so the two classes land +//! together. These tests run the instances, not a second traversal: `Array` +//! walks the compiler's index primitives, and `Maybe` and `Either` fold by +//! cases. + +use super::*; + +#[test] +fn monoid_identities_and_folds_run_under_wasmtime() { + let source = r#" +module Main where + +import Prelude +import Effect.Console (log) +import Data.Monoid (mempty) +import Data.Foldable (class Foldable, foldl, foldr, foldMap) +import Data.Maybe (Maybe(..)) +import Data.Either (Either(..)) + +data One a = One a + +instance foldableOne :: Foldable One where + foldr f z (One x) = f x z + foldl f z (One x) = f z x + foldMap f (One x) = f x + +checks :: Effect Unit +checks = do + log (foldr (\x acc -> x <> acc) ":" ["a", "b"]) + log (foldl (\acc x -> acc <> x) ":" ["a", "b"]) + log (foldMap (\x -> x <> "!") ["a", "b"]) + log (if arrayLength (mempty :: Array Int) == 0 then "empty-array" else "empty-array-wrong") + log (if (mempty :: Unit) == unit then "empty-unit" else "empty-unit-wrong") + log mempty + log (foldr (\x acc -> x <> acc) "z" (Right "r" :: Either Int String)) + log (foldr (\x acc -> x <> acc) "z" (Left 1 :: Either Int String)) + log (foldl (\acc x -> acc <> x) "z" (Just "m")) + log (foldMap (\x -> x) (Nothing :: Maybe String)) + log (foldMap (\x -> x) (One "one")) + pure unit + +main = let ignored = runEffect checks in 0 +"#; + let Some(output) = run_with_wasmtime(source) else { + eprintln!("skipping: wasmtime is not installed"); + return; + }; + assert_eq!(output.status.code(), Some(0), "{output:?}"); + assert_eq!( + output.stdout, + "ab:\n:ab\na!b!\nempty-array\nempty-unit\n\nrz\nz\nzm\n\none\n".as_bytes(), + "monoid identities and folds must match the class instances: {output:?}" + ); +} diff --git a/crates/psrs-driver/src/tests/mod.rs b/crates/psrs-driver/src/tests/mod.rs index 68d0cc63..35821a0a 100644 --- a/crates/psrs-driver/src/tests/mod.rs +++ b/crates/psrs-driver/src/tests/mod.rs @@ -10,11 +10,13 @@ mod data_function; mod deriving; mod effect_arity; mod effects; +mod foldable; mod guard_coverage; mod operators; mod partial_application; mod scalars; mod semigroup; +mod show; fn lower_source_to_mir(source: &str) -> psrs_backend::mir::Module { let core = lower_source_to_core("Main.purs", source).expect("source should lower to Core"); diff --git a/crates/psrs-driver/src/tests/module_loader.rs b/crates/psrs-driver/src/tests/module_loader.rs index 19a9f691..20ebe2ab 100644 --- a/crates/psrs-driver/src/tests/module_loader.rs +++ b/crates/psrs-driver/src/tests/module_loader.rs @@ -103,14 +103,17 @@ fn loads_the_standard_library_from_disk_in_trusted_order() { "Prelude", "Data.Function", "Data.Semigroup", + "Data.Monoid", "Data.Eq", "Data.Ord", "Data.Semiring", + "Data.Show", "Effect", "Effect.Console", "Test.Assert", "Data.Maybe", "Data.Either", + "Data.Foldable", "WASI.Resource", "WASI.IO", "WASI.Clock", @@ -128,14 +131,17 @@ fn loads_the_standard_library_from_disk_in_trusted_order() { path.ends_with("lib/Prelude.purs") || path.ends_with("lib/Data/Function.purs") || path.ends_with("lib/Data/Semigroup.purs") + || path.ends_with("lib/Data/Monoid.purs") || path.ends_with("lib/Data/Eq.purs") || path.ends_with("lib/Data/Ord.purs") || path.ends_with("lib/Data/Semiring.purs") + || path.ends_with("lib/Data/Show.purs") || path.ends_with("lib/Effect.purs") || path.ends_with("lib/Effect/Console.purs") || path.ends_with("lib/Test/Assert.purs") || path.ends_with("lib/Data/Maybe.purs") || path.ends_with("lib/Data/Either.purs") + || path.ends_with("lib/Data/Foldable.purs") || path.ends_with("lib/WASI/Resource.purs") || path.ends_with("lib/WASI/IO.purs") || path.ends_with("lib/WASI/Clock.purs") diff --git a/crates/psrs-driver/src/tests/show.rs b/crates/psrs-driver/src/tests/show.rs new file mode 100644 index 00000000..ad3af3ad --- /dev/null +++ b/crates/psrs-driver/src/tests/show.rs @@ -0,0 +1,112 @@ +//! The library's `Show` class. +//! +//! These tests execute under Wasmtime. The strings are the official `Show` +//! spellings: `true`/`false`, decimal `Int`, `unit`, quoted `Char` and +//! `String` (with the official escapes), arrays without spaces, and `Number` +//! with a trailing `.0` on an integer token. + +use super::*; + +#[test] +fn show_renders_the_instances_the_corpus_prints() { + let source = r#" +module Main where + +import Prelude +import Effect.Console (log) + +checks :: Effect Unit +checks = do + log (show true) + log (show false) + log (show 0) + log (show 42) + log (show (0 - 7)) + log (show ((0 - 2147483647) - 1)) + log (show unit) + log (show "hi") + log (show "a\"b") + log (show "line\n") + log (show 'a') + log (show '\n') + log (show '\'') + log (show [1, 2, 3]) + log (show [1.0, 2.0]) + log (show 0.0) + log (show 1.0) + log (show (numberNeg 2.0)) + log (show 1.5) + log (show 0.5) + log (show 0.25) + log (show 10000000000.0) + log (show 1.0e21) + log (show 1.0e-5) + log (show "é") + log (show "cafés") + pure unit + +main = let ignored = runEffect checks in 0 +"#; + let Some(output) = run_with_wasmtime(source) else { + eprintln!("skipping: wasmtime is not installed"); + return; + }; + assert_eq!(output.status.code(), Some(0), "{output:?}"); + let stdout = String::from_utf8_lossy(&output.stdout); + let expected = concat!( + "true\n", + "false\n", + "0\n", + "42\n", + "-7\n", + "-2147483648\n", + "unit\n", + "\"hi\"\n", + "\"a\\\"b\"\n", + "\"line\\n\"\n", + "'a'\n", + "'\\n'\n", + "'\\''\n", + "[1,2,3]\n", + "[1.0,2.0]\n", + "0.0\n", + "1.0\n", + "-2.0\n", + "1.5\n", + "0.5\n", + "0.25\n", + "10000000000.0\n", + "1e+21\n", + "0.00001\n", + "\"é\"\n", + "\"cafés\"\n", + ); + assert_eq!( + stdout.as_ref(), + expected, + "stdout:\n{stdout}\nstderr:\n{}", + String::from_utf8_lossy(&output.stderr) + ); +} + +#[test] +fn a_type_without_show_is_rejected() { + let source = r#" +module Main where + +import Prelude + +data Box = Box + +main :: String +main = show Box +"#; + let errors = check_program_types_lenient_with_prelude(&[("Main.purs", source)]) + .expect_err("Box is not a Show"); + assert!( + errors + .iter() + .any(|error| error.diagnostic.code == Some("NoInstanceFound")), + "expected NoInstanceFound, got {errors:?}" + ); +} diff --git a/docs/design/D-04-suite-roadmap.md b/docs/design/D-04-suite-roadmap.md index 92bcbdf1..f4b2f34f 100644 --- a/docs/design/D-04-suite-roadmap.md +++ b/docs/design/D-04-suite-roadmap.md @@ -210,10 +210,10 @@ failing agreement of **72/72**. Per-code agreement is `CannotDefinePrimModules` previous mismatches in `failing/881.purs` and `failing/InstanceSigsOrphanTypeDeclaration.purs`. -`passing` resolution is **235/413**, and its 178 first-stage blockers are **61 -missing library modules**, 113 other P3 resolution blockers, 160 P10 -entry-point-selection failures, 4 P0 lexing blockers, and **no P2 -surface-lowering blocker**. Relative to the clean 5298aad +`passing` resolution is **253/413**, and its 160 first-stage blockers are **57 +missing library modules**, 99 other P3 resolution blockers, and 4 P0 lexing +blockers, with **no P2 surface-lowering blocker**. The L6 stage table below +records where the files that do resolve later stop; 177 of them stop at P10. That 253/413 resolution, the 57 missing modules, and the 99 other P3 blockers are the `Show` measurement on `6f66524`. `Data.Foldable` measured the same counters independently on `6f66524` (resolution stays 245/413, missing modules 55, other P3 109). Those figures are not additive. Relative to the clean 5298aad baseline, the P2 count fell from 61 to zero: all 34 fixed issue-85 pattern blockers and 22 additional type, kind, instance, and declaration-form cases now pass P2, and #87 removed the last five — the negative type-level integer @@ -291,6 +291,38 @@ library-declared class, so it is also the reference pattern for a class with an operator alias, cross-module instance resolution, and a method body over compiler primitives. +**The `Show` class (2026-10-04)** adds `Data.Show` — the class, its `show` +method, and the `Boolean`, `Int`, `Number`, `Char`, `String`, `Unit`, and +`Array a` instances — and re-exports the class and method from `Prelude`. +Measured on this master before the slice (Eq, Ord, and Semiring already +landed) and after it, with +`PSRS_ORACLE=annotations cargo test -p psrs-driver --test suite -- --ignored --nocapture`: +L2 `passing` resolution moves from 245/413 to **253/413**, first-stage blockers +from 168 to 160, and other P3 blockers from 107 to 99. The missing-module count +stays 57. Eight files leave L2; `show` and `Show` are no longer first-blocker +names. The same after-run is the L6 table below (still 0/413 executing) and the +gate rows. `Number`'s digits are not a correctly rounded ECMAScript conversion, +so a file that compares `show` of a non-dyadic fraction with the official text +can still disagree once it runs. `logShow` and `assertEqual` stay with #95. + +**`Monoid` and `Foldable` (2026-10-04)** add `Data.Monoid` (`mempty` for +`String`, `Unit`, and `Array a`) and `Data.Foldable` (`foldr`, `foldl`, +`foldMap`, with `Array`, `Maybe`, and `Either a` instances). `fold` is not +declared: it is `foldMap` of the identity under a quantified `Foldable f`, and +projecting that rank-2 method from a dictionary parameter fails CC verification. +On `6f66524`, before this slice, the compile-path missing-module count is 57 and +P3 is 107; lenient `passing` resolution is already **245/413** (the 235 figure +above predates `Data.Eq`, `Data.Ord`, and `Data.Semiring`). The slice moves the +missing-module count from 57 to **55** and P3 from 107 to **109**. Resolution +stays 245/413. Of the seven files whose first blocker was `Data.Foldable` (4) or +`Data.Monoid` (3), five stop on a later missing module (`Data.Traversable` 2, +`Data.Tuple`, `Type.Proxy`, `Data.Monoid.Additive`) and two stop at another P3 +error. P10 stays 172, P5 typecheck 40, P5 kind check 17, P8 12, P6 4, and P0 4. +That base is `6f66524` without `Data.Show`. The two paragraphs measure the same +counters from the same base; the figures were measured independently on +`6f66524` and are not additive. + + ### M3 — Kinds and higher-kinded types - **Suite:** `KindsDoNotUnify` (24), `PartiallyAppliedSynonym` (12), @@ -323,10 +355,10 @@ environment, which owns `ClassInstanceArityMismatch`. The driver exposes a lenient kind check and the `l3` scoreboard; the scoreboard also runs against the vendored corpus without `purs`. -**Measured current result (annotations oracle, 2026-10-03):** M3 failing -agreement is **34/48**. Per code: `CycleInKindDeclaration` 2/2, +**Measured current result (annotations oracle, 2026-10-04):** M3 failing +agreement is **35/48**. Per code: `CycleInKindDeclaration` 2/2, `InfiniteKind` 2/2, `CycleInTypeSynonym` 3/4, `UndefinedTypeVariable` 3/4, -`PartiallyAppliedSynonym` 9/12, and `KindsDoNotUnify` 15/24. Fourteen expected +`PartiallyAppliedSynonym` 10/12, and `KindsDoNotUnify` 15/24. Thirteen expected diagnostics still differ: some are blocked by absent cross-module libraries such as `Data.Foldable`, `Data.Newtype`, `Effect.Console`, `Safe.Coerce`, or `Prim.*`; the rest need kind checking in expressions, polykinded instantiation, @@ -478,13 +510,15 @@ obligation to FE-13, and accepted mismatches still need type-checking fixes. - **Acceptance:** Agreement on the `errorCode`s above. - **Prerequisite:** M4. -**Measured current result (2026-10-04, annotations oracle):** **51/92** failing -cases agree. Per-code agreement is `OverlappingInstances` 8/8, -`NoInstanceFound` 40/53, `MissingClassMember` 2/2, `DuplicateInstance` 1/1, -and 0 for `PossiblyInfiniteInstance` (1), `OrphanInstance` (6), -`InvalidInstanceHead` (7), `InvalidNewtypeInstance` (5), -`DuplicateTypeClass` (1), `ClassInstanceArityMismatch` (1), and -`CannotDeriveInvalidConstructorArg` (7). The `<>` slice raises `NoInstanceFound` +**Measured current result (2026-10-04, annotations oracle, remeasured with +`Show`):** **53/84** failing cases agree. Per-code agreement is +`OverlappingInstances` 8/8, `NoInstanceFound` 41/52, `MissingClassMember` 2/2, +`DuplicateInstance` 1/1, `InvalidInstanceHead` 1/5, and 0 for +`PossiblyInfiniteInstance` (1), `OrphanInstance` (6), `InvalidNewtypeInstance` +(1), `DuplicateTypeClass` (1), and `CannotDeriveInvalidConstructorArg` (7). +The historical 51/92 figure below predates `Eq`/`Ord`/`Semiring` and this +slice; the 84 is the set of cases whose annotations are entirely M5 codes on +this tree. The `<>` slice raises `NoInstanceFound` from 38/53 to 40/53 while the aggregate stays 51/92: two cases that agreed under another code now agree here, so the total is a floor rather than a sum of the per-code moves. @@ -635,16 +669,32 @@ failure must reach the guest as a trap to be visible, which is the only execution signal the corpus can express. Nothing in the corpus needs argv, stdin, or a preopened directory, so the runner passes none. -The 413 rejections, by the first phase that blocks them, from the -`PSRS_REQUIRE_WASMTIME=1` L6/M7 scoreboard run on 2026-10-04: +The 413 rejections, by the first phase that blocks them. `Show` and `Data.Monoid`/`Data.Foldable` each remeasured this table on `6f66524`. The figures were measured independently on `6f66524` and are not additive. + +After `Show` (`PSRS_ORACLE=annotations` L6/M7 scoreboard run on 2026-10-04): | Blocker | Cases | Recovered by | | --- | --- | --- | -| Missing library module | 61 | Phase 3: #94 `Prelude`, #95 `Effect`/`Effect.Console`/`Test.Assert`, #96 tuples, `Proxy`, `Prim`, and #124 the unowned `Data.*` modules; 61 cases stop on a missing module. | -| P10 Wasm structuring | 160 | Reached the backend; 97 have a `main` that is not the zero-argument `Int` entry, and 63 have no selectable `main`. | -| P3 resolve | 113 | Another resolution error behind the library gap; the library surface owns most of them. | -| P5 typecheck | 43 | A type error behind the other blockers. | -| P5 kind check | 16 | A kind error behind the other blockers. | +| Missing library module | 57 | Phase 3: #94 `Prelude`, #95 `Effect`/`Effect.Console`/`Test.Assert`, #96 tuples, `Proxy`, `Prim`, and #124 the unowned `Data.*` modules; 57 cases stop on a missing module. | +| P10 Wasm structuring | 177 | Reached the backend; 114 have a `main` that is not the zero-argument `Int` entry, and 63 have no selectable `main`. | +| P3 resolve | 99 | Another resolution error behind the library gap; the library surface owns most of them. | +| P5 typecheck | 42 | A type error behind the other blockers. | +| P5 kind check | 17 | A kind error behind the other blockers. | +| P8 closure conversion | 13 | A representation behind the other blockers. | +| P0 lex | 4 | The DEC-16 lone-surrogate cases, which are also L1 differences. | +| P6 Core lowering | 4 | `Prim.undefined` has no runtime representation, plus one partially applied field constructor. | +| P2 surface lowering | 0 | No `passing` file stops in surface lowering; Phase 2 closed this row. | +| Harness loading | 0 | Nothing: every case assembles. | + +After `Data.Monoid` and `Data.Foldable` (`PSRS_REQUIRE_WASMTIME=1` compile-path measurement on 2026-10-04 at `6f66524`, taken without `Show`): + +| Blocker | Cases | Recovered by | +| --- | --- | --- | +| Missing library module | 55 | Phase 3: #94 `Prelude`, #95 `Effect`/`Effect.Console`/`Test.Assert`, #96 tuples, `Proxy`, `Prim`, and #124 the unowned `Data.*` modules; 55 cases stop on a missing module. | +| P10 Wasm structuring | 172 | Reached the backend. The earlier 97/63 entry-shape split was not re-counted. | +| P3 resolve | 109 | Another resolution error behind the library gap; the library surface owns most of them. | +| P5 typecheck | 40 | A type error behind the other blockers. | +| P5 kind check | 17 | A kind error behind the other blockers. | | P8 closure conversion | 12 | A representation behind the other blockers. | | P0 lex | 4 | The DEC-16 lone-surrogate cases, which are also L1 differences. | | P6 Core lowering | 4 | `Prim.undefined` has no runtime representation, plus one partially applied field constructor. | @@ -658,12 +708,27 @@ on the next library gap); P5 typecheck moved from 31 to 35 and P10 from 141 to 225/413, P3 from 138 to 123, and P10 from 147 to 155, as recorded in M2. The `<>` slice then moved resolution from 225/413 to 235/413, P3 from 123 to 113, P10 from 155 to 160, P5 typecheck from 40 to 43, P8 from 11 to 12, and P6 from 3 -to 4. The -remaining missing-module blockers are `Partial.Unsafe` (9), `Type.Proxy` (7), -`Data.Eq` (6), then `Data.Array`, `Unsafe.Coerce`, `Data.Monoid`, -`Data.Foldable`, and `Data.Symbol` (3 each), with 19 further modules at one or -two cases. Separately, the largest single *export* gap inside an existing module -is `Effect.Console.logShow` (17 cases), which needs the `Show` class. +to 4. Remeasured on `6f66524` before `Data.Monoid` and `Data.Foldable`, the +compile-path counts are missing-module 57, P10 172, P3 107, P5 typecheck 40, and +P5 kind check 17; lenient resolution is 245/413. This slice then moves +missing-module 57 to 55 and P3 107 to 109, and leaves resolution at 245/413. +The remaining missing-module blockers on that Foldable measurement are +`Partial.Unsafe` (9), `Type.Proxy` (8), then `Unsafe.Coerce`, `Data.Tuple`, +`Data.Symbol`, and `Data.Array` (3 each), `Data.Traversable`, `Data.Predicate`, +`Data.Newtype`, `Data.Generic.Rep`, and `Control.Monad.ST` (2 each), and 16 +further modules at one case. Separately, that measurement's largest single +*export* gap inside an existing module is `Effect.Console.logShow` (17 cases), +which needs the `Show` class. +Remeasured independently with `Show` on the same `6f66524` base, the remaining +missing-module blockers are `Partial.Unsafe` (9), `Type.Proxy` (7), +`Data.Foldable` (4), then `Data.Array`, `Unsafe.Coerce`, `Data.Monoid`, and +`Data.Symbol` (3 each), with the rest at one or two cases. `Data.Eq` is no +longer one of them. That measurement's largest single *export* gap inside an +existing module is `Effect.Console.logShow` (18 cases whose message is that the +name is not exported, plus 4 whose message is `unknown name`), which is #95's +wrapper over `Show` and is not defined in this slice. +The figures were measured independently on `6f66524` and are not additive. + The failure path is now landed rather than assumed: `Prelude.trap` is a `psrs:effect` external whose body is an unreachable path, `Test.Assert` writes a @@ -677,19 +742,23 @@ loader could not use an on-disk sibling. Before #86, two such cases were `passing/RedefinedFixity/M2.purs` and `M3.purs`; their imported `M1.purs` uses an operator alias. Both now pass module resolution. -`stdlib/lib` holds 16 modules now: `Prelude` (the `Effect` interface, including -`pure`, `bind`, `runEffect`, and `trap`), `Data.Function`, `Data.Semigroup` -(the `Semigroup` class, `append`, and `<>`), `Effect` and `Effect.Console` (the -corpus-facing names over that interface and `WASI.Console`), `Test.Assert` (the -four checks that need no class surface), `Data.Maybe`, `Data.Either`, and the -`WASI` services. The corpus imports `Effect.Console` 339 times, `Effect` 57 +`stdlib/lib/trusted` is the union of the two slices. Foldable recorded 22 +modules; `Data.Show` is the additional trusted module from the Show slice. +The list is `Prelude` (the `Effect` interface, including `pure`, `bind`, +`runEffect`, and `trap`), `Data.Function`, `Data.Semigroup` (the `Semigroup` +class, `append`, and `<>`), `Data.Monoid` (`mempty`), `Data.Eq`, `Data.Ord`, +`Data.Semiring`, `Data.Show` (`Show` and `show`), `Data.Foldable` (`foldr`, +`foldl`, `foldMap`), `Effect` and `Effect.Console` (the corpus-facing names +over that interface and `WASI.Console`), `Test.Assert` (the four checks that +need no class surface), `Data.Maybe`, `Data.Either`, and the `WASI` services. +The corpus imports `Effect.Console` 339 times, `Effect` 57 times, `Test.Assert` 29 times, then `Type.Proxy` (17), `Partial.Unsafe` (12), `Data.Tuple` (8), `Prim.Row` (7), and the `Prim.*` and `Data.*` hierarchies. -`Test.Assert` exports only `assert`, `assert'`, `assertTrue`, and `assertFalse`: -`assertEqual` needs the `Eq` and `Show` class surface (#94), and `assertThrows` -needs to observe a trap, which the target profile does not provide. `logShow` is -absent for the same reason `assertEqual`'s `Show` is absent, and it is the -largest single export gap in the library. +`Test.Assert` exports only `assert`, `assert'`, `assertTrue`, and `assertFalse`. +`Eq` and `Show` are declared; `assertEqual` and `logShow` are still #95's +exports and are not added here. `assertThrows` needs to observe a trap, which +the target profile does not provide. `logShow` remains the largest single +export gap in the library. ### M8 — Warnings and optimization @@ -786,7 +855,7 @@ concrete slice issues as sub-issues; this table is the index. | 0 | [#80](https://github.com/biuld/purescript-rs/issues/80) Lexer and layout agreement | `failing/2434.purs`, `layout/Commas.purs`, `layout/CaseGuards.purs` | Self-contained parse agreement. L1 is measured at 904/908; the four remaining cases are the DEC-16 intentional differences. String values follow [DEC-16](../decision/DEC-16-scalar-strings-and-utf8-storage.md); preserving lone UTF-16 surrogates is not a remaining gate. | | 1 | [#74](https://github.com/biuld/purescript-rs/issues/74) Make every gate measurable | [#81](https://github.com/biuld/purescript-rs/issues/81) official `errorCode` mapping, [#82](https://github.com/biuld/purescript-rs/issues/82) lenient type check and L4/L5 scoreboards, [#83](https://github.com/biuld/purescript-rs/issues/83) harness module path, [#93](https://github.com/biuld/purescript-rs/issues/93) runtime scoreboard | Nothing else can be verified until L4, L5, L6/M7, and M8-W report numbers. Changes no user-visible behavior. | | 2 | [#75](https://github.com/biuld/purescript-rs/issues/75) Frontend surface lowering — **complete** | [#84](https://github.com/biuld/purescript-rs/issues/84) ascription, [#85](https://github.com/biuld/purescript-rs/issues/85) patterns, [#86](https://github.com/biuld/purescript-rs/issues/86) operator aliases, [#87](https://github.com/biuld/purescript-rs/issues/87) type wildcards and rows, [#88](https://github.com/biuld/purescript-rs/issues/88) guards and multi-scrutinee `case`, [#89](https://github.com/biuld/purescript-rs/issues/89) `Prim` and unary minus, [#90](https://github.com/biuld/purescript-rs/issues/90) instance resolution | **No `passing` file stops in surface lowering.** From the 61-case `5298aad` baseline, #88 moved 22 past P2, the 34 fixed pattern paths plus 22 type, kind, instance, and declaration forms brought it to 5, and [#87](https://github.com/biuld/purescript-rs/pull/122) removed the last five. All six slices are `Done`. | -| 3 | [#76](https://github.com/biuld/purescript-rs/issues/76) Standard library — **next** | [#94](https://github.com/biuld/purescript-rs/issues/94) `Prelude` (80 measured, first blocking stage), [#95](https://github.com/biuld/purescript-rs/issues/95) `Effect`/`Effect.Console`/`Test.Assert` (305), [#96](https://github.com/biuld/purescript-rs/issues/96) tuples, `Proxy`, `Partial.Unsafe` (12), [#124](https://github.com/biuld/purescript-rs/issues/124) the `Data` modules no slice owned (25) | **342 of the 354** files L2 originally could not resolve were blocked on a missing library module. #95's first slice landed `Effect` and `Effect.Console`, dropping that to **83** and `passing` resolution from 59/413 to **199/413**; its second slice landed the `unit` value and `Test.Assert`, dropping the missing-module count to **62** and `passing` resolution to **209/413**. #94's first slice then landed `Data.Function` and the `$`/`#` operators, taking resolution to **225/413** and the missing-module count to **61**; its next slice landed `Data.Semigroup` and `<>`, taking resolution to **235/413**. What #95 still owns is `logShow` and `assertEqual`, both waiting on the `Show`/`Eq` class surface in #94. Depends on Phase 2, now complete: the library itself uses ascriptions, guards, sections, and instances. #94 measures 0 by first blocking stage only because `stdlib/lib/Prelude.purs` resolves; its missing surface is latent, surfacing as the P3 `show`, `Monad`, `Eq`, `identity`, and `Functor` blockers *behind* the library modules; its `Corpus cases` field now records that measured count. | +| 3 | [#76](https://github.com/biuld/purescript-rs/issues/76) Standard library — **next** | [#94](https://github.com/biuld/purescript-rs/issues/94) `Prelude` (54 measured by the `Show` slice, first blocking stage; Foldable left the earlier 80 and did not remeasure this counter), [#95](https://github.com/biuld/purescript-rs/issues/95) `Effect`/`Effect.Console`/`Test.Assert` (305), [#96](https://github.com/biuld/purescript-rs/issues/96) tuples, `Proxy`, `Partial.Unsafe` (12), [#124](https://github.com/biuld/purescript-rs/issues/124) the `Data` modules no slice owned (25) | **342 of the 354** files L2 originally could not resolve were blocked on a missing library module. #95's first slice landed `Effect` and `Effect.Console`, dropping that to **83** and `passing` resolution from 59/413 to **199/413**; its second slice landed the `unit` value and `Test.Assert`, dropping the missing-module count to **62** and `passing` resolution to **209/413**. #94's first slice then landed `Data.Function` and the `$`/`#` operators, taking resolution to **225/413** and the missing-module count to **61**; its next slice landed `Data.Semigroup` and `<>`, taking resolution to **235/413**. `Eq`, `Ord`, and `Semiring` then landed with the intrinsic layer. The `Show` slice, measured on that master, moves resolution from 245/413 to **253/413**. `Data.Monoid` and `Data.Foldable`, measured independently on `6f66524` before `Show`, leave lenient resolution at **245/413** and move the compile-path missing-module count from 57 to 55. The figures were measured independently on `6f66524` and are not additive. What #95 still owns is `logShow` and `assertEqual`; the classes they need are declared, and the wrappers are not part of either slice. Depends on Phase 2, now complete: the library itself uses ascriptions, guards, sections, and instances. #94 measures 0 by first blocking stage only because `stdlib/lib/Prelude.purs` resolves; its missing surface is latent, surfacing as the P3 `Monad`, `identity`, `Functor`, and `negate` blockers *behind* the library modules. After `Show`, 54 `passing` files still stop first on a Prelude class or value name (`Monad` 11, `identity` 8, `Functor` 6, `negate` 5, `&&`/`otherwise`/`<$>`/`<<<` 3 each, `Partial` 3, `when`/`mod` 2 each, and one each of `between`, `>>>`, `||`, `Applicative`, and `P.identity`). `show` and `Show` are not in that set. Foldable did not remeasure that Prelude-name counter. Show recorded that the GitHub `Corpus cases` field was not written because `gh` is not authenticated on that machine; Foldable's row said the field records the measured count, and that claim was not rechecked here. | | 4 | [#77](https://github.com/biuld/purescript-rs/issues/77) L4 and L5 to 100% | [#97](https://github.com/biuld/purescript-rs/issues/97) missing class checks, [#98](https://github.com/biuld/purescript-rs/issues/98) deriving and fundeps, [#99](https://github.com/biuld/purescript-rs/issues/99) hole inference, [#100](https://github.com/biuld/purescript-rs/issues/100) M3 kind gate, [#123](https://github.com/biuld/purescript-rs/issues/123) `forall` binder visibility | Turns "measurable" into "passing". #81 makes 153 cases trackable; the rest need rules. #123 is the shared root cause behind the three visible-type-application limits #87 recorded, and #100's polykind instantiation needs the same machinery, so it is filed as one foundational change rather than three patches. | | 5 | [#78](https://github.com/biuld/purescript-rs/issues/78) Backend on real programs | [#73](https://github.com/biuld/purescript-rs/issues/73) aggregate fixture execution, [#101](https://github.com/biuld/purescript-rs/issues/101) CC/MIR coverage | Consumes the output of Phases 2–4. The backend rows are `Partial` on source coverage, not on design. | | 6 | [#79](https://github.com/biuld/purescript-rs/issues/79) M8 warnings and optimization | [#91](https://github.com/biuld/purescript-rs/issues/91) warning scoreboard, [#92](https://github.com/biuld/purescript-rs/issues/92) optimize comparison | Last, because both need a harness first and neither blocks another phase. | @@ -869,14 +938,16 @@ for matrix status. | --- | --- | --- | --- | | L0 | Layout goldens | 15/15 official parse outcomes agree (12 accepted, 3 rejected), enforced by regression tests. | 15/15 agreement, with all layout cases covered by regression tests. | | L1 | Non-excluded parse behavior | 904/908 agreement using the annotations oracle; `passing` 410/413, `failing` 412/413, `warning` 67/67, `layout` 15/15, with the four remaining cases recorded as DEC-16 intentional differences | 100% agreement apart from the DEC-16 intentional differences. | -| L2 | Module, import, export, and name resolution | 72/72 failing cases; 235/413 passing modules resolve, with 61 blocked on a missing module, 113 at P3, 160 at P10, 4 at P0, and none at P2; no case is blocked on assembly. | The mapped resolution cases and all required passing-module cases agree. | -| L3 | Kinds and higher-kinded types | 34/48 failing cases; `KindsDoNotUnify` 15/24 and the other mapped code totals as measured in M3. | 100% agreement for the mapped kind cases. | +| L2 | Module, import, export, and name resolution | 72/72 failing cases; 253/413 passing modules resolve, with 57 blocked on a missing module, 99 at P3, 4 at P0, and none at P2; no case is blocked on assembly. L6, separately, stops 177 at P10. | The mapped resolution cases and all required passing-module cases agree. | +| L3 | Kinds and higher-kinded types | 35/48 failing cases; `KindsDoNotUnify` 15/24, `PartiallyAppliedSynonym` 10/12, and the other mapped code totals as measured in M3. | 100% agreement for the mapped kind cases. | | L4 | Core type checking | 35/50 failing cases; `TypesDoNotUnify` 32/41, `IntOutOfRange` 1/1, `InfiniteType` 2/2, `CannotApplyExpressionOfTypeOnType` 1/2, `EscapedSkolem` 0/2, `ExpectedType` 0/2, `AmbiguousTypeVariables` 0/1. | 100% agreement for the mapped type cases. | -| L5 | Classes and instances | 51/92 failing cases; `OverlappingInstances` 8/8, `NoInstanceFound` 40/53, `MissingClassMember` 2/2, `DuplicateInstance` 1/1, and 0 for the other mapped codes. | 100% agreement for the mapped class cases. | -| L6/M7 | Runtime and standard library | 0/413 non-FFI passing files compile, validate, and run; 61 stop on missing modules, 160 at P10, 113 at P3, 43 at P5 typecheck, 16 at P5 kind checking, 12 at P8, 4 at P6, and 4 at P0; no P2 surface-lowering blockers and no harness-loading blockers. | Every in-scope passing file for the feature compiles, validates, and runs with the expected result. | +| L5 | Classes and instances | 53/84 failing cases; `OverlappingInstances` 8/8, `NoInstanceFound` 41/52, `MissingClassMember` 2/2, `DuplicateInstance` 1/1, `InvalidInstanceHead` 1/5, and 0 for the other mapped codes. | 100% agreement for the mapped class cases. | +| L6/M7 | Runtime and standard library | 0/413 non-FFI passing files compile, validate, and run; 57 stop on missing modules, 177 at P10, 99 at P3, 42 at P5 typecheck, 17 at P5 kind checking, 13 at P8, 4 at P6, and 4 at P0; no P2 surface-lowering blockers and no harness-loading blockers. | Every in-scope passing file for the feature compiles, validates, and runs with the expected result. | | M8-W | Warnings | 67 non-FFI warning files are in scope; no warning-code scoreboard exists | Warning-code agreement reaches 100% for the tracked warning corpus. | | M8-O | Optimization | 10 optimize files are in scope; they are not vendored and their goldens are JavaScript output | Expected optimize/CoreFn output agrees for all tracked optimize files. | +The L2 and L6/M7 rows that name 253/413, 57 missing modules, 99 other P3 blockers, or 177 at P10 are the `Show` measurement on `6f66524`. `Data.Foldable`'s compile-path measurement of the same counters is the second M7 table and was taken independently on `6f66524`; the figures are not additive. Foldable did not rewrite these gate rows. The L5 53/84 figure is the `Show` remeasurement; Foldable left the earlier 51/92. + ### Feature-to-gate crosswalk This crosswalk makes the suite state part of each matrix row without repeating @@ -912,7 +983,7 @@ resolved, type checked, and represented in Typed Core as required. | ID | Feature | Current support | Status | Next landing | | --- | --- | --- | --- | --- | | FE-01 | Lexing, Unicode tokens, comments, literals, and layout | Lexer and layout agree with the L1 annotations scoreboard at 904/908, including 15/15 layout cases. The four differences are the DEC-16 intentional differences: a supplementary scalar is accepted as one `Char` (`failing/2434.purs`), and an unpaired surrogate escape is rejected in `StringEscapes.purs` and the two `StringEdgeCases` files. A paired surrogate escape decodes as one scalar, and no surrogate becomes U+FFFD. Parse agreement does not verify string values. | Partial | Cover the remaining literal forms the corpus exercises. | -| FE-02 | Module headers, imports, exports, qualified names, aliases, and hiding | Module graph, stable module IDs, value/type/constructor/class imports and exports, fixity aliases, virtual `Prim.*` type/class interfaces, instance dictionary identities, per-branch instance exports, and unary minus through ordinary `negate` resolution work in a subset; 72/72 mapped failing cases agree and 235/413 passing modules resolve. A re-exported operator alias carries its target's identity and does not require the target's name unless the target is declared in the re-exporting module. Class-only imports do not import methods into the value namespace; selective imports still receive visible instances through the module dependency graph. P3 checks explicit signatures and declaration dependencies; P5 checks inferred public schemes by stable type identity. `Prim.undefined` has a compiler-owned identity, type, and interface export, but Core lowering still rejects it because no runtime representation is defined. The [primitives topic](frontend/type-system/prim.md) owns the `Prim.*` inventory, the evidence-class dispatch order, relation outcomes, and diagnostic behavior; #120 adds the missing relation and report paths. Broader pattern-binding support remains incomplete. | Partial | Complete pattern-binding support; add the `Prim.undefined` runtime representation and continue official-suite coverage for primitive solving. | +| FE-02 | Module headers, imports, exports, qualified names, aliases, and hiding | Module graph, stable module IDs, value/type/constructor/class imports and exports, fixity aliases, virtual `Prim.*` type/class interfaces, instance dictionary identities, per-branch instance exports, and unary minus through ordinary `negate` resolution work in a subset; 72/72 mapped failing cases agree and 253/413 passing modules resolve. A re-exported operator alias carries its target's identity and does not require the target's name unless the target is declared in the re-exporting module. Class-only imports do not import methods into the value namespace; selective imports still receive visible instances through the module dependency graph. P3 checks explicit signatures and declaration dependencies; P5 checks inferred public schemes by stable type identity. `Prim.undefined` has a compiler-owned identity, type, and interface export, but Core lowering still rejects it because no runtime representation is defined. The [primitives topic](frontend/type-system/prim.md) owns the `Prim.*` inventory, the evidence-class dispatch order, relation outcomes, and diagnostic behavior; #120 adds the missing relation and report paths. Broader pattern-binding support remains incomplete. | Partial | Complete pattern-binding support; add the `Prim.undefined` runtime representation and continue official-suite coverage for primitive solving. | | FE-03 | Value declarations, signatures, recursive groups, pattern bindings, and `where` | Named declarations, signatures, recursive local groups, and top-level SCC inference work; selected local pattern declarations, including `LetPattern`, lower through the pattern pipeline. The full declaration and `where` forms are not end-to-end. | Partial | Complete remaining pattern declarations and local `where` blocks. | | FE-04 | Declaration forms: `data`, `newtype`, `type`, `class`, `instance`, `derive`, `foreign`, roles, fixities, and kind signatures | Data/newtype roles are inferred and checked, foreign role signatures enter the checked kind environment, and source role errors retain spans. Instance declarations resolve into dictionary-scoped members; signatures associate with consecutive equations, reject orphan/repeated declaration groups, and check against the class method specialized by the instance head. Deriving and several declaration forms remain incomplete. | Partial | Complete deriving and the remaining declaration-form semantics. | | FE-05 | Expressions: application, operators, lambdas, `if`, `let`, `case`, records, arrays, literals, sections, `do`, and `ado` | Application, value and type operators with resolved fixities, the `Data.Function` application operators `$` and `#` with their official associativity and precedence, unary minus through the ordinary in-scope `negate` value, lambdas, `if`, `let`, `case`, scalar arrays, empty array literals whose element type is determined, records, and selected literals work; `do`/`ado` lower to bind, discard, and `let`. The ascription `e :: T` is checked against its written type and remains explicit through Typed Core. Sections lower through P4 and have runtime coverage. Remaining literal and expression forms are open. | Partial | Complete the remaining literal and expression forms. | @@ -924,13 +995,13 @@ resolved, type checked, and represented in Typed Core as required. | FE-11 | Kinds, kind signatures, higher-kinded types, kind annotations, and kind variables | Dedicated kind inference/checking covers several declarations, annotations, records/rows, and official kind errors. Kind checking runs once per program and produces the checked kind and role environment every module's type check consumes, with each diagnostic attributed to the module that declares the offending type and a missing scheme reported instead of inferred; one primitive table is the only reading of a primitive's kind, and the `Coercible` solver consumes that shared denotation and kind solver. A declaration's scheme quantifies only the kind unknowns its own definition leaves undetermined, but the well-scoped-quantification rule (`QuantificationCheckFailureInKind`) is not implemented, and an instance head whose class is declared in another module is still skipped. | Partial | Add the well-scoped-quantification rule, cross-module instance heads, kind checking in expressions, and type-level row functions. | | FE-12 | Algebraic data types, constructors, newtypes, and constructor typing | Data/newtype declarations, constructor schemes, constructor application, and basic case typing work. | Partial | Add full recursive/parameterized checking, exhaustiveness, and all pattern forms. | | FE-13 | Records, row types, row polymorphism, and variants | Closed concrete records, field access/update, partial source record patterns, exact generated product patterns, and row unification work. The row normalizer returns collected fields and tail or an `InvalidShape` diagnostic; it does not treat an unrecognized shape as a closed row. The `Prim.Row` and `Prim.RowList` relations use that normalizer and the shared kind solver. #87 tracks row syntax under FE-17; #120 covers the primitive row constraints under FE-14. Rigid-tail row unification still has known bugs. Labels are scalar sequences under [DEC-16](../decision/DEC-16-scalar-strings-and-utf8-storage.md); the current compiler still uses Rust strings and does not preserve lone surrogates. A label that exists only in an unknown tail is not accessed or updated. Open rows still have no runtime layout, and variants are not implemented. | Partial | Fix rigid-tail row unification; complete row syntax and official coverage, then add variants without choosing a runtime field layout. | -| FE-14 | Constraints, type classes, superclasses, class members, and instances | Source constraint elaboration, contextual and multi-parameter instances, superclass evidence, imported generic dictionaries, ordered source instance chains, and rank-1 polymorphic method signatures execute. Superclass edges use `TypeTemplate` substitution, including constructed arguments such as `C (Array a)`; `a_superclass_edge_over_a_constructed_argument_runs_when_wasmtime_is_available` executes the `Gamma (Array a)` edge through an `Epsilon Int` use. Instance member signatures are associated with consecutive equations, resolve instance-head variables, and are checked against specialized class method types; source and Wasmtime tests cover specialized and more-general signatures plus mismatches. A constrained instance-member annotation cannot yet be solved solely to specialize it to a monomorphic expected method type. Duplicate member groups, orphan member signatures, duplicate named instances, and ordinary-value/name collisions use official diagnostics. Explicit export lists filter instance branches by class/head/context visibility while preserving identity and chain positions; imports do not require the class in their selective list to receive visible instances. Class-only imports keep methods out of the ordinary value namespace. Constrained-forall expression ascriptions are checked in a complete rollback probe, then elaborated at their expected use type; ordinary class dictionaries survive both monomorphic and rank-N uses, with actual Wasmtime execution evidence. #120 adds compiler-owned rules for the twelve `Prim.Row`, `Prim.RowList`, `Prim.Symbol`, and `Prim.Int` relations and a report path for `Prim.TypeError.Fail`, `Warn`, and `Partial`. Open-row `Lacks` and `Union` preserve partial evidence and re-enter solving on residual constraints; the outcome contract belongs to each rule, while the shared framework checks returned dictionary arguments and bounds speculative work. `Warn` propagates through explicit scoped dictionaries; otherwise it emits at the enclosing value or instance declaration and discharges, so inferred schemes do not retain a `Warn` context; `Fail` renders a valid `Doc` as a custom error, while malformed `Doc` falls back to `NoInstanceFound`. `Partial` still lacks HIR exhaustiveness metadata and therefore reports the generic no-instance message. Five `Coercible` board mismatches remain because the proof evidence has no relation arguments for the shared dictionary verification path. The [primitives topic](frontend/type-system/prim.md) owns these semantics and their measured coverage. A wildcard in an instance head is rejected as `InvalidInstanceHead`; a wildcard in an instance context stays legal because the constraint is still solvable. Scoped method-local constraints and quantified method parameters have evidence in the [rank-N acceptance record](../implementation/frontend/rank-n.md). The standard library now declares its first class: `Data.Semigroup` defines `Semigroup`, its `append` method, and the `<>` alias, with `String`, `Unit`, and `Array a` instances resolved across the module graph and re-exported from `Prelude`. | Partial | Reconcile remaining class-rule mismatches and official-suite coverage; deriving is tracked under FE-16. | +| FE-14 | Constraints, type classes, superclasses, class members, and instances | Source constraint elaboration, contextual and multi-parameter instances, superclass evidence, imported generic dictionaries, ordered source instance chains, and rank-1 polymorphic method signatures execute. Superclass edges use `TypeTemplate` substitution, including constructed arguments such as `C (Array a)`; `a_superclass_edge_over_a_constructed_argument_runs_when_wasmtime_is_available` executes the `Gamma (Array a)` edge through an `Epsilon Int` use. Instance member signatures are associated with consecutive equations, resolve instance-head variables, and are checked against specialized class method types; source and Wasmtime tests cover specialized and more-general signatures plus mismatches. A constrained instance-member annotation cannot yet be solved solely to specialize it to a monomorphic expected method type. Duplicate member groups, orphan member signatures, duplicate named instances, and ordinary-value/name collisions use official diagnostics. Explicit export lists filter instance branches by class/head/context visibility while preserving identity and chain positions; imports do not require the class in their selective list to receive visible instances. Class-only imports keep methods out of the ordinary value namespace. Constrained-forall expression ascriptions are checked in a complete rollback probe, then elaborated at their expected use type; ordinary class dictionaries survive both monomorphic and rank-N uses, with actual Wasmtime execution evidence. #120 adds compiler-owned rules for the twelve `Prim.Row`, `Prim.RowList`, `Prim.Symbol`, and `Prim.Int` relations and a report path for `Prim.TypeError.Fail`, `Warn`, and `Partial`. Open-row `Lacks` and `Union` preserve partial evidence and re-enter solving on residual constraints; the outcome contract belongs to each rule, while the shared framework checks returned dictionary arguments and bounds speculative work. `Warn` propagates through explicit scoped dictionaries; otherwise it emits at the enclosing value or instance declaration and discharges, so inferred schemes do not retain a `Warn` context; `Fail` renders a valid `Doc` as a custom error, while malformed `Doc` falls back to `NoInstanceFound`. `Partial` still lacks HIR exhaustiveness metadata and therefore reports the generic no-instance message. Five `Coercible` board mismatches remain because the proof evidence has no relation arguments for the shared dictionary verification path. The [primitives topic](frontend/type-system/prim.md) owns these semantics and their measured coverage. A wildcard in an instance head is rejected as `InvalidInstanceHead`; a wildcard in an instance context stays legal because the constraint is still solvable. Scoped method-local constraints and quantified method parameters have evidence in the [rank-N acceptance record](../implementation/frontend/rank-n.md). The standard library now declares its first classes: `Data.Semigroup` defines `Semigroup`, its `append` method, and the `<>` alias, with `String`, `Unit`, and `Array a` instances resolved across the module graph and re-exported from `Prelude`. `Data.Monoid` adds `mempty` for those three types, `Data.Show` adds the `Show` class and `show` for `Boolean`, `Int`, `Number`, `Char`, `String`, `Unit`, and `Array a`, and `Data.Foldable` adds `foldr`, `foldl`, and `foldMap` for `Array`, `Maybe`, and `Either a`. `fold` is not part of that surface yet, because a polymorphic `foldMap` of the identity does not lower. | Partial | Reconcile remaining class-rule mismatches and official-suite coverage; deriving is tracked under FE-16. | | FE-15 | Functional dependencies | Source fundep improvement uses transitive determining closure and selected branches; independent class arguments still prove apartness. Ambiguity and consistency diagnostics are covered. One known divergence: a type wildcard standing for a fundep-determined position is reported as a fundep conflict, where `purs` only warns — `passing/WildcardInInstance.purs` says so in its own comment. It is unmeasurable until Phase 3 provides `Effect` and `Effect.Console`. | Partial | Reconcile official-suite fundep coverage and remaining advanced class forms. | | FE-16 | Deriving, roles, `Coercible`, and newtype-based derivation | Role inference/checking (including imported aliases), compiler-owned `Coercible` solving, checked kind compatibility, higher-kinded given rewriting, canonical open-row alignment, constructor-visibility checks, explicit Typed Core evidence boundaries, and backend-planned conversions work for the covered subset. Source and Wasmtime tests cover phantom/nominal/representational roles, parameterized newtype scalar/function/array payloads, structural `Eq`/`Ord`, nested alias-aware `Functor.map`, direct `Bifunctor.bimap`, recursive `Eq`, checked `derive newtype` adapters, empty-class underlying-instance validation, and cross-module dictionaries. Differential tests also cover function-result traversal, `Contravariant` via `Profunctor.lcmap`, and resolved re-exported class identity. Runtime closure capture still blocks the function-based `Contravariant` case; other upstream deriving classes and open-row runtime conversion remain incomplete. Scoped method-local constraints on class methods are covered under FE-18. The `Coercible` solver reads primitive kinds through the shared kind checker; expression-level kind checking remains incomplete. Five `Coercible` board mismatches remain because a `Proof` member has no dictionary arguments for the framework's decided-argument check, so its answer still needs an explicit verification contract. | Partial | Implement the remaining upstream deriving classes; expand closure-capture runtime support and remaining coercion cases. See [roles and coercions acceptance](../implementation/frontend/roles-and-coercions.md). | | FE-17 | Visible type application, typed binders, type wildcards, holes, and advanced annotations | Typed binders preserve and check scoped annotations, and each source type wildcard receives fresh kind/type variables through the shared type spine. Type-level `String` and `Int` literals are ordinary spine nodes: a signature may contain them, they unify by value, and they survive into THIR where the verifier compares them. A wildcard in a value signature is solved by unification and is accepted in every shape `purs` accepts; a wildcard in an instance head is rejected as `InvalidInstanceHead`, while one in an instance context stays legal. The `1664.purs` wildcard binder lowers through P2. Visible term type application, wildcard warning/error behavior, higher-kinded application, and non-generalized hole diagnostics remain incomplete. The `Type`, `Constraint`, and `Symbol` heads are accepted as ordinary type constructors with their declared primitive kinds. Official's CST has no kind-application node; its kind checker synthesizes `KindApp` while instantiating a polymorphic kind, and this compiler performs that instantiation in the kind solver, so its source type spine needs no `KindApplication` node. The source forms that do name a kind or type explicitly are separate nodes. #87 lands both of the forms that blocked P2: a negative type-level integer prefix is the negative literal on the shared spine, and a visible type application `e @T` is elaborated by the checker, which substitutes the written argument for the operand's outermost quantifier after checking it against that quantifier's kind, and is erased at runtime. No P2 surface-lowering case remains. Three limits are recorded rather than approximated. A chained application `f @A @B` is reported, because the quantifiers an application leaves behind are scheme variables here and choosing between them needs the scheme to record which variables a visible application has consumed. A visible application on a class-method head is unresolved, which is `failing/ClassHeadNoVTA3.purs`. And this compiler's CST does not carry the binder visibility that official's `CST/Convert.hs` derives from `forall @a.`, so a plain `forall a.` binder is selectable where `purs` rejects it — the permissive direction, and the remaining half of `failing/VisibleTypeApplications1.purs`. `CannotApplyExpressionOfTypeOnType` and `CannotSkipTypeApplication` are the mapped codes. The primitive row relations themselves all have rules, and the row-side gap that remains is the rigid-tail unification defect under FE-13. | Partial | Model `forall` binder visibility so a visible application matches official, then resolve chained applications and class-method heads. | | FE-18 | Higher-rank types, subsumption, impredicativity, and higher-rank `forall` | Bidirectional checking preserves nested quantifiers, checks directional function/record subsumption, and rejects escaping skolems and specialized universal arguments. Source and GC execution cases cover rank-2 through rank-4, fields, returned and captured values, recursive annotations, higher-kinded parameters, and nested constraints. See the [rank-N acceptance record](../implementation/frontend/rank-n.md) for verification evidence and the official differential battery. | Partial | Reconcile the complete official higher-rank/skolem corpus, including its library dependencies and separate higher-rank kind requirements; track visible type application and diagnostic agreement. | | FE-19 | Foreign declarations and target-aware external names | Source-declared WIT bindings are resolved for the supported backend path. `foreign import data` is a nominal opaque type with no constructors; a nullary one maps to a WIT resource. THIR and Core keep it as `Constructor(User(id))` plus `opaque_ids`, distinct from `Int` (`lowers_an_opaque_foreign_type_to_core_without_collapsing_it_to_int`). JavaScript FFI is not a frontend target. CC/MIR handle layout is not done. | Partial | Finish target-aware foreign value rules beyond the supported WIT subset. Resource lifetime and handle layout stay in the backend. | -| FE-20 | Warnings, holes, source spans, and official diagnostic codes | Source spans exist and resolution, kind, type, and class `errorCode`s are measured: L1 904/908, L2 72/72, L3 34/48, L4 35/50, L5 51/92. The L4/L5 denominators count cases reaching their owner stage; 16 cases in the combined run are blocked earlier. Pattern-binder diagnostics match the annotated duplicate-name cases; warning coverage and complete diagnostic agreement remain open. Non-generalized hole diagnostics remain tracked under FE-17. | Partial | Add the missing class checks (#97) and track warning-code agreement separately from acceptance errors. | +| FE-20 | Warnings, holes, source spans, and official diagnostic codes | Source spans exist and resolution, kind, type, and class `errorCode`s are measured: L1 904/908, L2 72/72, L3 35/48, L4 35/50, L5 53/84. The L4/L5 denominators count cases reaching their owner stage; 16 cases in the combined run are blocked earlier. Pattern-binder diagnostics match the annotated duplicate-name cases; warning coverage and complete diagnostic agreement remain open. Non-generalized hole diagnostics remain tracked under FE-17. | Partial | Add the missing class checks (#97) and track warning-code agreement separately from acceptance errors. | | FE-21 | Typed Core normalization and CoreFn/optimization compatibility | Typed Core lowering and verification work for the supported subset; official optimize output is not yet a target. | Partial | Add Core optimization passes and an explicit optimize compatibility track. | The frontend landing order is: diff --git a/docs/design/D-15-compiler-builtins.md b/docs/design/D-15-compiler-builtins.md index 1ca6b68f..564e21b0 100644 --- a/docs/design/D-15-compiler-builtins.md +++ b/docs/design/D-15-compiler-builtins.md @@ -391,6 +391,13 @@ The remaining surface operators `-`, `/`, and `%` are still bound to the `Int` intrinsics directly. `Data.Ring` and the Euclidean division class are the follow-up. +`Show` is a library class in `Data.Show`, re-exported from `Prelude`, over the +same primitives. It does not add an intrinsic: integer, character, and string +rendering are written in the source language, and `Number` rendering is too. +That `Number` spelling is not a correctly rounded ECMAScript conversion. A pure +numeric formatter with canonical inputs and outputs is the open capability +question above, not a new `Intrinsic`. + ## References - [D-01 — Frontend and IR Boundaries](D-01-frontend-and-ir-boundaries.md). diff --git a/docs/design/backend/wasm/wasi-platform-library.md b/docs/design/backend/wasm/wasi-platform-library.md index d0e4a2f3..56082982 100644 --- a/docs/design/backend/wasm/wasi-platform-library.md +++ b/docs/design/backend/wasm/wasi-platform-library.md @@ -127,6 +127,8 @@ wrapper owns the corpus-facing name. | `Prelude` | `Effect`, `pure`, `bind`, `discard`, `map`, `apply`, `runEffect`, `trap`, and the re-exported `class Semigroup`, `append`, `<>`, `const`, `flip`, `$`, `#` | the `psrs:effect` interface the compiler synthesizes bodies for, and `Data.Function` / `Data.Semigroup` | FE-02, FE-05, FE-09, FE-14, BE-02, BE-21 | | `Data.Function` | `apply`, `applyFlipped`, `const`, `flip`, `on`, `$`, `#` | nothing: it is the definition site, and `$`/`#` are its fixity aliases | FE-05 | | `Data.Semigroup` | `class Semigroup`, `append`, `<>` | nothing: it is the definition site for the class, and `append` for `String` is built from the compiler's `stringToBytes` / `arrayAppend` / `bytesToString`; `append` for `Array a` is `arrayAppend` | FE-14, BE-10 | +| `Data.Monoid` | `class Monoid`, `mempty` | `Data.Semigroup`; `String`, `Unit`, and `Array a` identities | FE-14 | +| `Data.Foldable` | `class Foldable`, `foldr`, `foldl`, `foldMap` | `Data.Monoid` and the array index primitives; `Array`, `Maybe`, and `Either a` instances | FE-14, FE-16 | | `Effect` | re-exports the `Prelude` surface above | `Prelude` | FE-02 | | `Effect.Console` | `log`, `warn`, `error` | `WASI.Console` | BE-21 | | `Test.Assert` | `assert`, `assert'`, `assertTrue`, `assertFalse` | `Effect.Console.error` and `Prelude.trap` | BE-21, BE-27 | @@ -222,11 +224,12 @@ constant, which exists to give the entry its declared `i32` result The platform library is source code under `stdlib/lib`, read from disk and resolved, type-checked, and linked like any module. `stdlib/lib/trusted` fixes the trusted prefix order (`Prelude`, `Data.Function`, `Data.Semigroup`, -`Effect`, `Effect.Console`, `Test.Assert`, `Data.Maybe`, `Data.Either`, +`Data.Monoid`, `Data.Eq`, `Data.Ord`, `Data.Semiring`, `Effect`, +`Effect.Console`, `Test.Assert`, `Data.Maybe`, `Data.Either`, `Data.Foldable`, `WASI.Resource`, `WASI.IO`, `WASI.Clock`, `WASI.Random`, `WASI.Console`, `WASI.Process`, `WASI.FileSystem`, `WASI.Network`, `WASI`). `Data.Function` declares the application operators and their fixities; `Data.Semigroup` -declares the `Semigroup` class, its method, and the `<>` alias; `Prelude` +declares the `Semigroup` class, its method, and the `<>` alias; `Data.Monoid` declares `Monoid` and `mempty` for `String`, `Unit`, and `Array a`; `Data.Foldable` declares `Foldable` with `foldr`, `foldl`, and `foldMap` for `Array`, `Maybe`, and `Either a`; `Prelude` re-exports `$`, `#`, `const`, `flip`, and `Data.Semigroup`'s `class Semigroup`, `append`, and `<>`, which is the official `Prelude`'s own re-export list. `Data.Maybe` and `Data.Either` are ordinary library @@ -435,8 +438,9 @@ with execution tests. `WASI.Network` wraps the socket services and lowers; it has no execution test, and HTTP/TLS are not implemented, so their capability flags stay disabled in the default profile. The standard library is read from `stdlib/lib` at runtime (`stdlib/lib/trusted` lists `Prelude`, `Data.Function`, -`Data.Semigroup`, `Effect`, `Effect.Console`, `Test.Assert`, `Data.Maybe`, -`Data.Either`, `WASI.Resource`, `WASI.IO`, `WASI.Clock`, `WASI.Random`, +`Data.Semigroup`, `Data.Monoid`, `Data.Eq`, `Data.Ord`, `Data.Semiring`, +`Effect`, `Effect.Console`, `Test.Assert`, `Data.Maybe`, `Data.Either`, +`Data.Foldable`, `WASI.Resource`, `WASI.IO`, `WASI.Clock`, `WASI.Random`, `WASI.Console`, `WASI.Process`, `WASI.FileSystem`, `WASI.Network`, and `WASI` in trusted-prefix order). The driver discovers user modules from the entry files' directories (`psrs_driver::load_program_files`): it indexes sibling `.purs` diff --git a/stdlib/lib/Data/Foldable.purs b/stdlib/lib/Data/Foldable.purs new file mode 100644 index 00000000..5b54e8b0 --- /dev/null +++ b/stdlib/lib/Data/Foldable.purs @@ -0,0 +1,66 @@ +-- | The `Foldable` class. +-- | +-- | This is the class surface deriving and the fundep work assume: `foldr`, +-- | `foldl`, and `foldMap`, not a second traversal implementation. The `Array` +-- | instance walks indexes with the compiler's `arrayLength` and `arrayIndex` +-- | primitives. `Maybe` and `Either` fold by cases. `fold` is omitted. It would be `foldMap` of the identity under a +-- | quantified -- | `Foldable f`, and projecting that rank-2 method from a dictionary parameter +-- | fails CC verification, which rejects every program that does not prune +-- | unreachable library declarations. Combinators that need +-- | `Applicative`, `Alt`, or `Ord` stay out until those classes exist. +module Data.Foldable + ( class Foldable + , foldr + , foldl + , foldMap + ) where + +import Data.Either (Either(..)) +import Data.Maybe (Maybe(..)) +import Data.Monoid (class Monoid, mempty) +import Data.Semigroup (class Semigroup, append) + +-- | A container that can be folded. +class Foldable f where + foldr :: forall a b. (a -> b -> b) -> b -> f a -> b + foldl :: forall a b. (b -> a -> b) -> b -> f a -> b + foldMap :: forall a m. Monoid m => (a -> m) -> f a -> m + +instance foldableArray :: Foldable Array where + foldr f z xs = foldrIndex f z xs 0 + foldl f z xs = foldlIndex f z xs 0 + foldMap f xs = foldr (\x acc -> append (f x) acc) mempty xs + +instance foldableMaybe :: Foldable Maybe where + foldr _ z Nothing = z + foldr f z (Just x) = f x z + foldl _ z Nothing = z + foldl f z (Just x) = f z x + foldMap _ Nothing = mempty + foldMap f (Just x) = f x + +instance foldableEither :: Foldable (Either a) where + foldr _ z (Left _) = z + foldr f z (Right x) = f x z + foldl _ z (Left _) = z + foldl f z (Right x) = f z x + foldMap _ (Left _) = mempty + foldMap f (Right x) = f x + +-- | Left-to-right index, right-to-left combination: `go i` is +-- | `f xs[i] (go (i + 1))`, which is `foldr`. `intLt` is the internal +-- | comparison, so this module does not import the `Ord` operator. +foldrIndex :: forall a b. (a -> b -> b) -> b -> Array a -> Int -> b +foldrIndex f z xs i = + if intLt i (arrayLength xs) then + f (arrayIndex xs i) (foldrIndex f z xs (intAdd i 1)) + else + z + +-- | Left-to-right walk. The recursive call is in tail position. +foldlIndex :: forall a b. (b -> a -> b) -> b -> Array a -> Int -> b +foldlIndex f z xs i = + if intLt i (arrayLength xs) then + foldlIndex f (f z (arrayIndex xs i)) xs (intAdd i 1) + else + z diff --git a/stdlib/lib/Data/Monoid.purs b/stdlib/lib/Data/Monoid.purs new file mode 100644 index 00000000..9698f4d3 --- /dev/null +++ b/stdlib/lib/Data/Monoid.purs @@ -0,0 +1,25 @@ +-- | The `Monoid` class. +-- | +-- | `Monoid` is the superclass constraint on `Data.Foldable.foldMap` and +-- | `fold`. The class and its `mempty` method are the official surface; the +-- | instances are the three types whose `Semigroup` instances already exist, +-- | so folding those types does not invent a second appending operation. +module Data.Monoid + ( class Monoid + , mempty + ) where + +import Data.Semigroup (class Semigroup) + +-- | A `Semigroup` with an identity element. +class Semigroup m <= Monoid m where + mempty :: m + +instance monoidString :: Monoid String where + mempty = "" + +instance monoidUnit :: Monoid Unit where + mempty = unit + +instance monoidArray :: Monoid (Array a) where + mempty = [] diff --git a/stdlib/lib/Data/Show.purs b/stdlib/lib/Data/Show.purs new file mode 100644 index 00000000..40dae577 --- /dev/null +++ b/stdlib/lib/Data/Show.purs @@ -0,0 +1,280 @@ +-- | The `Show` class. +-- | +-- | `show` renders a value as text. The `Int`, `Number`, `Boolean`, `Char`, +-- | `String`, `Unit`, and `Array` instances are the ones the corpus actually +-- | applies `show` to. `Show Unit` lives here rather than in `Data.Unit` +-- | because that module is not part of this library yet; a record instance is +-- | not here because it needs `reflectSymbol`, which has no runtime. +-- | +-- | `Boolean`, `Int`, `Char`, `String`, `Unit`, and `Array` match the official +-- | spelling, including the `Char`/`String` escapes. `Number` uses the same +-- | shape as the official instance — decimal digits, `.0` on an integer token, +-- | scientific form outside `(1e-6, 1e21)` — but the digits come from the +-- | numeric primitives, not from a correctly rounded ECMAScript conversion. +-- | Integers whose absolute value is below `1e21`, powers of ten, and the +-- | dyadic fractions the digit loop reaches exactly match; other fractions +-- | print a deterministic expansion that can differ from `purs`. +module Data.Show + ( class Show + , show + ) where + +import Data.Semigroup ((<>)) + +-- | A type that can be rendered as text. +class Show a where + show :: a -> String + +instance showBoolean :: Show Boolean where + show value = if value then "true" else "false" + +instance showInt :: Show Int where + show value = + if intEq value minInt then "-2147483648" + else if intLt value 0 then "-" <> showPositiveInt (intNeg value) + else showPositiveInt value + +instance showNumber :: Show Number where + show value = + if numberNe value value then "NaN" + else if numberEq value positiveInfinity then "Infinity" + else if numberEq value negativeInfinity then "-Infinity" + else if numberLt value 0.0 then "-" <> showPositiveNumber (numberNeg value) + else showPositiveNumber value + +instance showChar :: Show Char where + show value = "'" <> escapeCode (charToInt value) false false <> "'" + +instance showString :: Show String where + show value = "\"" <> escapeBytes (stringToBytes value) 0 <> "\"" + +instance showUnit :: Show Unit where + show _ = "unit" + +instance showArray :: Show a => Show (Array a) where + show value = "[" <> showElements value 0 <> "]" + +minInt :: Int +minInt = (0 - 2147483647) - 1 + +positiveInfinity :: Number +positiveInfinity = numberDiv 1.0 0.0 + +negativeInfinity :: Number +negativeInfinity = numberDiv (numberNeg 1.0) 0.0 + +showPositiveInt :: Int -> String +showPositiveInt value = + if intLt value 10 then digit value + else showPositiveInt (intDiv value 10) <> digit (intMod value 10) + +digit :: Int -> String +digit value = bytesToString [intAdd 48 value] + +-- | A non-negative `Number`. Integers below `1e21` print as decimal digits +-- | plus `.0`. Smaller magnitudes print as a fixed expansion. The rest use +-- | scientific form, which is what the official `Show Number` does past +-- | those thresholds. +showPositiveNumber :: Number -> String +showPositiveNumber value = + if numberEq value 0.0 then "0.0" + else if numberGe value 1.0e21 then showScientific value + else if numberLt value 1.0e-6 then showScientific value + else if numberEq value (floorNumber value) then showIntegerNumber value <> ".0" + else showMixed value + +showMixed :: Number -> String +showMixed value = + let + whole = floorNumber value + fraction = numberSub value whole + wholeText = if numberEq whole 0.0 then "0" else showIntegerNumber whole + fractionText = digitsToString (trimZeros (collectFraction fraction 17 [])) 0 + in + if intEq (arrayLength (stringToBytes fractionText)) 0 then wholeText <> ".0" + else wholeText <> "." <> fractionText + +showScientific :: Number -> String +showScientific value = + let + exponent = powerOfTen value 0 + mantissa = scaleToUnit value exponent + in + showMantissa mantissa <> showExponent exponent + +showMantissa :: Number -> String +showMantissa value = + let + whole = numberToInt (floorNumber value) + fraction = numberSub value (intToNumber whole) + fractionText = digitsToString (trimZeros (collectFraction fraction 17 [])) 0 + in + if intEq (arrayLength (stringToBytes fractionText)) 0 then showPositiveInt whole + else showPositiveInt whole <> "." <> fractionText + +showExponent :: Int -> String +showExponent value = + if intLt value 0 then "e-" <> showPositiveInt (intNeg value) + else "e+" <> showPositiveInt value + +powerOfTen :: Number -> Int -> Int +powerOfTen value exponent = + if numberEq value 0.0 then exponent + else if numberGe value 10.0 then powerOfTen (numberDiv value 10.0) (intAdd exponent 1) + else if numberLt value 1.0 then powerOfTen (numberMul value 10.0) (exponent - 1) + else exponent + +scaleToUnit :: Number -> Int -> Number +scaleToUnit value exponent = + if intEq exponent 0 then value + else if intGt exponent 0 then scaleToUnit (numberDiv value 10.0) (exponent - 1) + else scaleToUnit (numberMul value 10.0) (intAdd exponent 1) + +-- | Floor of a non-negative number. Groups of nine digits stay inside `Int`, +-- | so a value past `2^31` still floors without a wider primitive. +floorNumber :: Number -> Number +floorNumber value = + if numberLt value 1000000000.0 then intToNumber (numberToInt value) + else + let + billions = floorNumber (numberDiv value 1000000000.0) + base = numberMul billions 1000000000.0 + low = numberToInt (numberSub value base) + in + numberAdd base (intToNumber low) + +showIntegerNumber :: Number -> String +showIntegerNumber value = + if numberLt value 1000000000.0 then showPositiveInt (numberToInt value) + else + let + billions = floorNumber (numberDiv value 1000000000.0) + base = numberMul billions 1000000000.0 + low = numberToInt (numberSub value base) + in + showIntegerNumber billions <> pad9 low + +pad9 :: Int -> String +pad9 value = + if intLt value 10 then "00000000" <> showPositiveInt value + else if intLt value 100 then "0000000" <> showPositiveInt value + else if intLt value 1000 then "000000" <> showPositiveInt value + else if intLt value 10000 then "00000" <> showPositiveInt value + else if intLt value 100000 then "0000" <> showPositiveInt value + else if intLt value 1000000 then "000" <> showPositiveInt value + else if intLt value 10000000 then "00" <> showPositiveInt value + else if intLt value 100000000 then "0" <> showPositiveInt value + else showPositiveInt value + +collectFraction :: Number -> Int -> Array Int -> Array Int +collectFraction fraction remaining digits = + if intEq remaining 0 then digits + else if numberEq fraction 0.0 then digits + else + let + scaled = numberMul fraction 10.0 + digitValue = numberToInt scaled + rest = numberSub scaled (intToNumber digitValue) + in + collectFraction rest (remaining - 1) (arrayAppend digits [digitValue]) + +trimZeros :: Array Int -> Array Int +trimZeros digits = + let + length = arrayLength digits + in + if intEq length 0 then digits + else if intEq (arrayIndex digits (length - 1)) 0 then trimZeros (takePrefix digits (length - 1)) + else digits + +takePrefix :: Array Int -> Int -> Array Int +takePrefix digits count = copyPrefix digits count 0 + +copyPrefix :: Array Int -> Int -> Int -> Array Int +copyPrefix digits count index = + if intGe index count then [] + else arrayAppend [arrayIndex digits index] (copyPrefix digits count (intAdd index 1)) + +digitsToString :: Array Int -> Int -> String +digitsToString digits index = + if intGe index (arrayLength digits) then "" + else digit (arrayIndex digits index) <> digitsToString digits (intAdd index 1) + +showElements :: forall a. Show a => Array a -> Int -> String +showElements values index = + if intGe index (arrayLength values) then "" + else if intEq index 0 then show (arrayIndex values index) <> showElements values (intAdd index 1) + else "," <> show (arrayIndex values index) <> showElements values (intAdd index 1) + +-- | The body of a `Char` or `String` escape, without the surrounding quotes. +-- | `ampersand` is true when a numeric escape must not swallow a following +-- | digit, which is the official `\&` rule. +escapeCode :: Int -> Boolean -> Boolean -> String +escapeCode code ampersand inString = + if intEq code 7 then "\\a" + else if intEq code 8 then "\\b" + else if intEq code 12 then "\\f" + else if intEq code 10 then "\\n" + else if intEq code 13 then "\\r" + else if intEq code 9 then "\\t" + else if intEq code 11 then "\\v" + else if intLt code 32 then "\\" <> showPositiveInt code <> emptyAmpersand ampersand + else if intEq code 127 then "\\127" <> emptyAmpersand ampersand + else if intEq code 92 then "\\\\" + else if intEq code 34 then if inString then "\\\"" else utf8 code + else if intEq code 39 then if inString then utf8 code else "\\'" + else utf8 code + +emptyAmpersand :: Boolean -> String +emptyAmpersand needed = if needed then "\\&" else "" + +escapeBytes :: Array Int -> Int -> String +escapeBytes bytes index = + if intGe index (arrayLength bytes) then "" + else + let + decoded = decode bytes index + code = arrayIndex decoded 0 + next = arrayIndex decoded 1 + in + escapeCode code (nextIsDigit bytes next) true <> escapeBytes bytes next + +nextIsDigit :: Array Int -> Int -> Boolean +nextIsDigit bytes index = + if intGe index (arrayLength bytes) then false + else + let + code = arrayIndex (decode bytes index) 0 + in + if intLt code 48 then false else intLe code 57 + +-- | One Unicode scalar and the index of the byte after it. The bytes are +-- | well-formed UTF-8 because they came from `stringToBytes`. +decode :: Array Int -> Int -> Array Int +decode bytes index = + let + first = arrayIndex bytes index + in + if intLt first 128 then [first, intAdd index 1] + else if intLt first 224 then + [ intAdd (intShl (intAnd first 31) 6) (continuation bytes (intAdd index 1)) + , intAdd index 2 + ] + else if intLt first 240 then + [ intAdd (intAdd (intShl (intAnd first 15) 12) (intShl (continuation bytes (intAdd index 1)) 6)) (continuation bytes (intAdd index 2)) + , intAdd index 3 + ] + else + [ intAdd (intAdd (intAdd (intShl (intAnd first 7) 18) (intShl (continuation bytes (intAdd index 1)) 12)) (intShl (continuation bytes (intAdd index 2)) 6)) (continuation bytes (intAdd index 3)) + , intAdd index 4 + ] + +continuation :: Array Int -> Int -> Int +continuation bytes index = intAnd (arrayIndex bytes index) 63 + +utf8 :: Int -> String +utf8 code = + if intLt code 128 then bytesToString [code] + else if intLt code 2048 then bytesToString [intOr 192 (intShr code 6), intOr 128 (intAnd code 63)] + else if intLt code 65536 then bytesToString [intOr 224 (intShr code 12), intOr 128 (intAnd (intShr code 6) 63), intOr 128 (intAnd code 63)] + else bytesToString [intOr 240 (intShr code 18), intOr 128 (intAnd (intShr code 12) 63), intOr 128 (intAnd (intShr code 6) 63), intOr 128 (intAnd code 63)] diff --git a/stdlib/lib/Effect/Console.purs b/stdlib/lib/Effect/Console.purs index 975fb98c..194fd227 100644 --- a/stdlib/lib/Effect/Console.purs +++ b/stdlib/lib/Effect/Console.purs @@ -7,12 +7,10 @@ -- | That keeps one place that decides where output goes, rather than a -- | wrapper that could drift from it. -- | --- | `logShow` is **absent**, not approximated. Its official type is --- | `forall a. Show a => a -> Effect Unit`, and no module in this library --- | declares a `Show` class yet, so providing it would mean either inventing a --- | second notion of stringification or special-casing the handful of types the --- | corpus happens to print. Both are the narrower special case the iteration --- | principles reject. It lands with the class surface in #94. +-- | `logShow` is **absent**, not approximated. `Data.Show` declares the class +-- | it needs (`forall a. Show a => a -> Effect Unit` is `log` of `show`), but +-- | the wrapper is the remaining `Effect.Console` surface and stays with #95 +-- | rather than being slipped in beside the class. module Effect.Console (log, warn, error) where import Prelude diff --git a/stdlib/lib/Prelude.purs b/stdlib/lib/Prelude.purs index d1f2752c..b26e3173 100644 --- a/stdlib/lib/Prelude.purs +++ b/stdlib/lib/Prelude.purs @@ -50,6 +50,8 @@ module Prelude , mul , (+) , (*) + , class Show + , show , const , flip , ($) @@ -61,6 +63,7 @@ import Data.Semigroup (class Semigroup, append, (<>)) import Data.Eq (class Eq, eq, notEq, (==), (/=)) import Data.Ord (class Ord, lessThan, lessThanOrEq, greaterThan, greaterThanOrEq, (<), (<=), (>), (>=)) import Data.Semiring (class Semiring, add, mul, (+), (*)) +import Data.Show (class Show, show) foreign import data Effect :: Type -> Type diff --git a/stdlib/lib/Test/Assert.purs b/stdlib/lib/Test/Assert.purs index 7e08ea4e..e4279f0c 100644 --- a/stdlib/lib/Test/Assert.purs +++ b/stdlib/lib/Test/Assert.purs @@ -10,12 +10,10 @@ -- | -- | **Deliberately absent**, with the reason recorded rather than approximated: -- | --- | - `assertEqual` and `assertEqual'` compare with `Eq` and print with `Show`, --- | and no module in this library declares either class yet. Providing them --- | would mean inventing a second notion of equality or of stringification, --- | or special-casing the types the corpus happens to compare. Both classes --- | land with the `Prelude` class surface (#94); the derived `Eq` rule also --- | requires the class to be declared in `Data.Eq`, which is #124. +-- | - `assertEqual` and `assertEqual'` compare with `Eq` and print with `Show`. +-- | Both classes are declared (`Data.Eq`, `Data.Show`); the functions stay +-- | with #95, which owns the rest of this module. Providing them here would +-- | take that slice's exports. `assertThrows` is a separate gap, below. -- | - `assertThrows` and `assertThrows'` need to observe that evaluating an -- | argument failed. A trap is not observable from inside the guest without -- | the Wasm exceptions proposal, which is outside the target profile diff --git a/stdlib/lib/trusted b/stdlib/lib/trusted index ed2470bb..65b4add8 100644 --- a/stdlib/lib/trusted +++ b/stdlib/lib/trusted @@ -3,14 +3,17 @@ Prelude Data.Function Data.Semigroup +Data.Monoid Data.Eq Data.Ord Data.Semiring +Data.Show Effect Effect.Console Test.Assert Data.Maybe Data.Either +Data.Foldable WASI.Resource WASI.IO WASI.Clock