Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions crates/psrs-driver/src/tests/assertions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,40 @@ fn assert_true_and_assert_false_report_the_value_that_did_not_hold() {
"Assertion failed: Expected: true\nActual: false\n",
);
}

/// The renderings `Data.Show` produces, so this fails if `logShow` grows a
/// stringifier of its own instead of composing the library `show`.
///
/// The negative case is written `0 - 7` rather than `- 7` on purpose: unary
/// minus resolves through an ordinary in-scope `negate`, which this library
/// does not declare yet, and this test is about the `Show` rendering.
#[test]
fn log_show_writes_the_library_rendering() {
let source = r#"
module Main where

import Prelude
import Effect.Console (logShow)

checks :: Effect Unit
checks = do
logShow 42
logShow (0 - 7)
logShow "hi"
logShow 'c'
logShow true
logShow [1, 2, 3]
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, b"42\n-7\n\"hi\"\n'c'\ntrue\n[1,2,3]\n",
"`logShow` must write exactly what `Data.Show.show` produces: {output:?}"
);
}
85 changes: 67 additions & 18 deletions docs/design/D-04-suite-roadmap.md

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions docs/design/backend/wasm/primitive-ffi-and-stdlib.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ stdlib/lib/
Data/Function.purs const, flip, apply, applyFlipped, on, $, #
Data/Semigroup.purs class Semigroup, append, <>
Effect.purs the corpus-facing name for that interface
Effect/Console.purs log, warn, error over WASI.Console
Effect/Console.purs log, warn, error over WASI.Console; logShow over show
Test/Assert.purs assert, assert', assertTrue, assertFalse
Data/Maybe.purs data Maybe a = Nothing | Just a, plus eliminators
Data/Either.purs data Either a b = Left a | Right b, plus eliminators
Expand All @@ -373,8 +373,10 @@ crates/psrs-backend/src/
`exitWithCode :: Int -> Effect Unit` and does not export `exitWithCodeRaw`.
The `WASI` umbrella re-exports the curated API of every focused module.
- The corpus-facing modules wrap that surface rather than adding imports:
`Effect.Console` re-exports `WASI.Console` under the names the suite imports,
and `Test.Assert` composes `Effect.Console.error` with `Prelude.trap`. A
`Effect.Console` re-exports `WASI.Console` under the names the suite imports
and adds `logShow` as `log` of the library's `show`, so it is a wrapper over
`Data.Show` and not a second stringifier. `Test.Assert` composes
`Effect.Console.error` with `Prelude.trap`. A
failure is a guest trap because that is the only signal a compilable corpus
case can carry; `Prelude.trap` is a `psrs:effect` external, not a host call.
- Each raw binding is an unexported `foreign import` whose parameters and
Expand Down
2 changes: 1 addition & 1 deletion docs/design/backend/wasm/wasi-platform-library.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ wrapper owns the corpus-facing name.
| `Data.Foldable` | `class Foldable`, `foldr`, `foldl`, `foldMap` | `Data.Monoid` and the array index primitives; `Array`, `Maybe`, and `Either a` instances | FE-14, FE-16 |
| `Data.Tuple` | `Tuple`, `fst`, `snd`, `curry`, `uncurry`, `swap` | the closed record `{ _1 :: a, _2 :: b }` that FE-06 already lowers a tuple to; `type Tuple a b` is that record, not an algebraic `data Tuple a b = Tuple a b` | FE-06 |
| `Effect` | re-exports the `Prelude` surface above | `Prelude` | FE-02 |
| `Effect.Console` | `log`, `warn`, `error` | `WASI.Console` | BE-21 |
| `Effect.Console` | `log`, `warn`, `error`, `logShow` | `WASI.Console`, and the library `Data.Show.show` for `logShow` | BE-21 |
| `Test.Assert` | `assert`, `assert'`, `assertTrue`, `assertFalse` | `Effect.Console.error` and `Prelude.trap` | BE-21, BE-27 |

### Capability matrix
Expand Down
3 changes: 2 additions & 1 deletion docs/implementation/backend/wasi-platform.md
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ WASI-10:
a_failed_assertion_traps_with_its_message_when_wasmtime_is_available,
a_failed_assertion_writes_the_message_before_it_traps,
a_statement_after_a_failed_assertion_never_runs,
assert_true_and_assert_false_report_the_value_that_did_not_hold};
assert_true_and_assert_false_report_the_value_that_did_not_hold,
log_show_writes_the_library_rendering};
tests::wasi::{prints_hello_world_when_wasmtime_is_available,
reads_the_monotonic_clock_when_wasmtime_is_available,
rejects_an_import_of_unexported_exit_with_code_raw,
Expand Down
18 changes: 12 additions & 6 deletions stdlib/lib/Effect/Console.purs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@
-- | That keeps one place that decides where output goes, rather than a
-- | wrapper that could drift from it.
-- |
-- | `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
-- | `logShow` adds no I/O of its own: it is `log` of `Data.Show.show`, so the
-- | rendering is the library's `Show` and the destination is still the one
-- | `WASI.Console.log` decides. It is a wrapper like `log`, not a second
-- | stringifier.
module Effect.Console (log, warn, error, logShow) where

import Prelude
import WASI.Console (error, log, warn)
import WASI.Console (error, log, warn)
import Data.Show (class Show, show)

-- | Writes the `Show` rendering of a value. `log` already writes the newline,
-- | so this is `log` composed with the library's `show`.
logShow :: forall a. Show a => a -> Effect Unit
logShow value = log (show value)
17 changes: 14 additions & 3 deletions stdlib/lib/Test/Assert.purs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,20 @@
-- | **Deliberately absent**, with the reason recorded rather than approximated:
-- |
-- | - `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.
-- | Both classes are declared (`Data.Eq`, `Data.Show`), so the surface itself
-- | is writable — the official signature is what this compiler cannot yet
-- | elaborate. The corpus calls the record form
-- | (`assertEqual' "label" { expected: e, actual: a }`), whose official type is
-- | `forall a. Eq a => Show a => String -> { actual :: a, expected :: a } ->
-- | Effect Unit`. A constraint whose quantified variable appears inside a
-- | record type is elaborated with the *record* as the constraint's argument,
-- | so `Eq a` is wanted for `{ actual :: a, expected :: a }` and the
-- | declaration is rejected with `NoInstanceFound`. The same signature with a
-- | type synonym for the record fails identically, so it is constraint
-- | elaboration rather than the record syntax. Approximating the signature
-- | would change the official API the corpus calls, so the functions stay out
-- | until that is fixed. #137 carries the minimal reproduction and the probes
-- | that separate this defect from record syntax.
-- | - `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
Expand Down
Loading