iter pd.3: prelude.ail.json retired — milestone prelude-decouple closed
Deleted examples/prelude.ail.json (-559 lines). The cross-form-identity preflight test that ratified pd.3's load-bearing assumption at module hash 3abe0d3fa3c11c99 has discharged its purpose and was removed along with its supporting bytes; the long-term prelude_parse_yields_canonical_hash anchor stays. The ail-CLI migrate-bare-cross-module-refs subcommand's defensive include + lockstep skip-branch removed; the rewrite logic's prelude-fallback capability silently retired with the synthetic insert (known debt, doc-comment updated to reflect). One in-mod core test relocated to crates/ailang-core/tests/workspace_pin.rs (pd.2.4 dev-dep cycle precedent — in-mod call to ailang_surface still structurally impossible). carve_out_inventory.rs: 8 → 7 carve-outs, §C4(b) row dropped, header sentence updated. form-a-default-authoring.md §C4(b) gets a "RETIRED 2026-05-14 by milestone prelude-decouple" status marker; original historical text preserved. New prelude_decouple_carve_out_pin.rs asserts examples/prelude.ail.json does NOT exist. Milestone prelude-decouple closed: prelude exists on disk only as examples/prelude.ail; ailang-core embeds zero prelude bytes; CLAUDE.md "authors write .ail" doctrine holds without exception. cargo test --workspace 573 green; bench/cross_lang.py + bench/compile_check.py exit 0; bench/check.py exit 1 with the established noise envelope (14th observation of bench_list_sum.bump_s since audit-cma; pd.3 is filesystem + spec text + test deletion only, no codegen / runtime / typecheck path edited — baseline pristine). Folds in the orphan pd.2 INDEX entry that was left uncommitted in the working tree.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"iter_id": "pd.3",
|
||||
"date": "2026-05-14",
|
||||
"mode": "standard",
|
||||
"outcome": "DONE",
|
||||
"tasks_total": 8,
|
||||
"tasks_completed": 8,
|
||||
"reloops_per_task": {
|
||||
"1": 0,
|
||||
"2": 0,
|
||||
"3": 0,
|
||||
"4": 0,
|
||||
"5": 0,
|
||||
"6": 0,
|
||||
"7": 0,
|
||||
"8": 0
|
||||
},
|
||||
"review_loops_spec": 0,
|
||||
"review_loops_quality": 1,
|
||||
"blocked_reason": null,
|
||||
"notes": "Task 1 implementer-phase had one structural correction loop (call-site swap → relocation) when the plan-prescribed mechanism failed to typecheck across the dev-dep cycle; counted as an in-phase repair, not a re-loop. Task 1 quality phase had one Minor (verbose stub comment); fixed inline by trimming, then re-approved — counted as the single quality re-loop. Task 4 + Task 6 had RED-on-purpose intermediate states (carve-out inventory test fails until Task 7 deletes the file; carve-out pin RED-on-purpose until same); both flipped GREEN at Task 7 as planned — not counted as failures. Task 3 doc-comment edit on MigrateCanonicalTypes was an unrequested-but-justified correction (capability-claim falsification by the strict task work); marked DONE_WITH_CONCERNS. pd.3 closes the prelude-decouple milestone (pd.1 + pd.2 + pd.3)."
|
||||
}
|
||||
+5
-21
@@ -231,9 +231,11 @@ enum Cmd {
|
||||
///
|
||||
/// Disambiguation: the first import (in declaration order) that
|
||||
/// owns a matching type wins. Ties are NOT diagnosed — the
|
||||
/// migration silently picks the first match. Prelude is consulted
|
||||
/// as an implicit last-resort fallback when no listed import owns
|
||||
/// the name.
|
||||
/// migration silently picks the first match. (The prelude
|
||||
/// last-resort fallback was retired in pd.3 alongside the
|
||||
/// `prelude.ail.json` embed; bare cross-module refs that resolve
|
||||
/// only via the prelude are now left bare and surfaced by the
|
||||
/// validator instead.)
|
||||
MigrateCanonicalTypes {
|
||||
/// Directory containing `*.ail.json` to migrate (typically
|
||||
/// `examples/`).
|
||||
@@ -469,20 +471,6 @@ fn main() -> Result<()> {
|
||||
modules.insert(m.name.clone(), (path, m));
|
||||
}
|
||||
|
||||
// Include the embedded prelude (so the implicit fallback
|
||||
// matches the loader's behaviour).
|
||||
let prelude_json: &str = include_str!(
|
||||
"../../../examples/prelude.ail.json"
|
||||
);
|
||||
let prelude: Module = serde_json::from_str(prelude_json)
|
||||
.expect("embedded prelude must parse");
|
||||
if !modules.contains_key("prelude") {
|
||||
modules.insert(
|
||||
"prelude".to_string(),
|
||||
(PathBuf::new(), prelude),
|
||||
);
|
||||
}
|
||||
|
||||
// Pre-pass: per-module local-types index.
|
||||
let mut local_types: BTreeMap<String, BTreeSet<String>> =
|
||||
BTreeMap::new();
|
||||
@@ -497,12 +485,8 @@ fn main() -> Result<()> {
|
||||
}
|
||||
|
||||
// Pass 2: rewrite each module's bare cross-module refs.
|
||||
// Skip the synthetic prelude entry (no path to write to).
|
||||
let mut rewritten = 0usize;
|
||||
for (mod_name, (path, m)) in modules.iter_mut() {
|
||||
if mod_name == "prelude" && path.as_os_str().is_empty() {
|
||||
continue;
|
||||
}
|
||||
let import_names: Vec<String> =
|
||||
m.imports.iter().map(|i| i.module.clone()).collect();
|
||||
let owning = mod_name.clone();
|
||||
|
||||
@@ -2630,39 +2630,12 @@ mod tests {
|
||||
assert!(modules.contains_key("main"));
|
||||
}
|
||||
|
||||
/// pd.1 Task 2: `build_workspace` accepts a pre-assembled modules
|
||||
/// map (with the caller having already injected any implicit
|
||||
/// modules) and runs the three-stage validation pipeline. The
|
||||
/// `implicit_imports` slice is what the diagnostic helpers consult
|
||||
/// for fallback candidate suggestions (Task 3 wires it through).
|
||||
#[test]
|
||||
fn build_workspace_accepts_assembled_modules_and_runs_validation() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
write_module(dir.path(), "main", &[]);
|
||||
let entry = dir.path().join("main.ail.json");
|
||||
|
||||
let (entry_name, root_dir, mut modules) =
|
||||
load_modules_with(&entry, load_one).expect("load");
|
||||
|
||||
// Caller-side prelude inject — pd.2 deletes `load_prelude` from
|
||||
// production, but this test still needs SOMETHING in the
|
||||
// `prelude` slot to demonstrate the caller-injects pattern.
|
||||
// Read the JSON sidecar via the same path the retired
|
||||
// `load_prelude` used. Surface owns the production inject path
|
||||
// (via `ailang_surface::parse_prelude`).
|
||||
const PRELUDE_JSON: &str =
|
||||
include_str!("../../../examples/prelude.ail.json");
|
||||
let prelude: Module = serde_json::from_str(PRELUDE_JSON)
|
||||
.expect("examples/prelude.ail.json must parse as a Module");
|
||||
modules.insert("prelude".to_string(), prelude);
|
||||
|
||||
let ws = build_workspace(entry_name, root_dir, modules, &["prelude"])
|
||||
.expect("build");
|
||||
|
||||
assert_eq!(ws.entry, "main");
|
||||
assert!(ws.modules.contains_key("main"));
|
||||
assert!(ws.modules.contains_key("prelude"));
|
||||
}
|
||||
// Note: tests that need a real prelude module via
|
||||
// `ailang_surface::parse_prelude()` live in
|
||||
// `crates/ailang-core/tests/workspace_pin.rs` (integration crate)
|
||||
// because the dev-dep cycle (`ailang-core` -> `ailang-surface` ->
|
||||
// `ailang-core`) leaves the lib-test target with two distinct
|
||||
// `ailang-core` compilations whose `Module` types do not unify.
|
||||
|
||||
fn module_with_bare_classref(name: &str, class_ref: &str) -> Module {
|
||||
// A module containing a single Def::Instance whose `class` field is
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
//! Carve-out inventory check — §T4 of the form-a-default-authoring
|
||||
//! spec. Asserts that exactly the eight named carve-out files exist
|
||||
//! spec. Asserts that exactly the seven named carve-out files exist
|
||||
//! under `examples/*.ail.json` after milestone close. The list is
|
||||
//! hardcoded; any change is a deliberate, brainstorm-level decision.
|
||||
//!
|
||||
//! Eight carve-outs at iter form-a.1 close:
|
||||
//! Seven carve-outs post prelude-decouple (2026-05-14):
|
||||
//! - §C4 (a) subject-matter: 7 fixtures (canonical-form rejection / unbound / class-def rejection).
|
||||
//! - §C4 (b) compile-time-embed: 1 fixture (prelude.ail.json).
|
||||
//!
|
||||
//! When the prelude-embed-refactor milestone retires §C4 (b), this
|
||||
//! test's pass-count drops to seven and `EXPECTED` is re-published.
|
||||
//! - §C4 (b) compile-time-embed: retired 2026-05-14 by milestone
|
||||
//! prelude-decouple; the prelude is now embedded as `prelude.ail`
|
||||
//! in `ailang-surface` and parsed at compile time via
|
||||
//! `ailang_surface::parse_prelude`.
|
||||
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
@@ -21,8 +21,6 @@ const EXPECTED: &[&str] = &[
|
||||
"test_ct1_bad_qualifier.ail.json",
|
||||
"test_ct1_bare_xmod_rejected.ail.json",
|
||||
"test_ct1_qualified_class_rejected.ail.json",
|
||||
// §C4 (b) — compile-time-embed
|
||||
"prelude.ail.json",
|
||||
];
|
||||
|
||||
fn examples_dir() -> std::path::PathBuf {
|
||||
|
||||
@@ -21,6 +21,22 @@ use ailang_core::workspace::{Registry, RegistryEntry, WorkspaceLoadError};
|
||||
use ailang_surface::load_workspace;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
// Adapter so this integration crate can call `load_modules_with` with
|
||||
// the same Io/Schema mapping the in-mod test helper used.
|
||||
fn load_one_adapter(path: &Path) -> Result<ailang_core::ast::Module, WorkspaceLoadError> {
|
||||
match ailang_core::load_module(path) {
|
||||
Ok(m) => Ok(m),
|
||||
Err(ailang_core::Error::Io(e)) => Err(WorkspaceLoadError::Io {
|
||||
path: path.to_path_buf(),
|
||||
source: e,
|
||||
}),
|
||||
Err(e) => Err(WorkspaceLoadError::Schema {
|
||||
path: path.to_path_buf(),
|
||||
source: e,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
fn examples_dir() -> PathBuf {
|
||||
let manifest_dir = env!("CARGO_MANIFEST_DIR");
|
||||
Path::new(manifest_dir).parent().unwrap().parent().unwrap().join("examples")
|
||||
@@ -486,3 +502,42 @@ fn ct1_fixture_qualified_class_orphan_post_mq1() {
|
||||
other => panic!("expected OrphanInstance, got {other:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// pd.1 Task 2 + pd.3 Task 1 relocation: `build_workspace` accepts a
|
||||
/// pre-assembled modules map (with the caller having already injected
|
||||
/// any implicit modules) and runs the three-stage validation pipeline.
|
||||
/// The `implicit_imports` slice is what the diagnostic helpers consult
|
||||
/// for fallback candidate suggestions. Relocated from the in-mod
|
||||
/// `tests` of `ailang-core/src/workspace.rs` because the caller-side
|
||||
/// prelude inject now uses `ailang_surface::parse_prelude()`, which
|
||||
/// only typechecks across the dev-dep edge from the integration-test
|
||||
/// crate (the lib-test crate sees two distinct `ailang-core`
|
||||
/// compilations and fails to unify the `Module` types).
|
||||
#[test]
|
||||
fn build_workspace_accepts_assembled_modules_and_runs_validation() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
write_simple_module_json(dir.path(), "main", &[]);
|
||||
let entry = dir.path().join("main.ail.json");
|
||||
|
||||
let (entry_name, root_dir, mut modules) =
|
||||
ailang_core::workspace::load_modules_with(&entry, load_one_adapter)
|
||||
.expect("load");
|
||||
|
||||
// Caller-side prelude inject — surface owns the production inject
|
||||
// path; this test consumes it via the dev-dep edge to demonstrate
|
||||
// the caller-injects pattern with the same source of truth.
|
||||
let prelude = ailang_surface::parse_prelude();
|
||||
modules.insert("prelude".to_string(), prelude);
|
||||
|
||||
let ws = ailang_core::workspace::build_workspace(
|
||||
entry_name,
|
||||
root_dir,
|
||||
modules,
|
||||
&["prelude"],
|
||||
)
|
||||
.expect("build");
|
||||
|
||||
assert_eq!(ws.entry, "main");
|
||||
assert!(ws.modules.contains_key("main"));
|
||||
assert!(ws.modules.contains_key("prelude"));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
//! pd.3 (`prelude-decouple` milestone): pin that
|
||||
//! `examples/prelude.ail.json` does NOT exist on disk. Surfaces
|
||||
//! immediately if the file is recreated by accident or a future
|
||||
//! refactor reverts the embed-source swap.
|
||||
//!
|
||||
//! Companion to the `examples_ail_json_inventory_matches_carve_outs`
|
||||
//! test in `crates/ailang-core/tests/carve_out_inventory.rs`: that
|
||||
//! one asserts the carve-out list is exactly seven; this one
|
||||
//! asserts the eighth (retired) file specifically does not exist.
|
||||
//! Either test detects an accidental re-introduction; this one
|
||||
//! pinpoints the file by name in the error message.
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
#[test]
|
||||
fn prelude_ail_json_does_not_exist_on_disk() {
|
||||
let path = Path::new("../../examples/prelude.ail.json");
|
||||
assert!(
|
||||
!path.exists(),
|
||||
"examples/prelude.ail.json must NOT exist after the prelude-\
|
||||
decouple milestone (closed 2026-05-14). The prelude is now \
|
||||
embedded as prelude.ail in ailang-surface. If you intentionally \
|
||||
re-introduced this file, you must also: (a) remove this pin, \
|
||||
(b) restore the §C4 (b) entry in carve_out_inventory.rs, \
|
||||
(c) revert the §C4 (b) status marker in \
|
||||
docs/specs/2026-05-13-form-a-default-authoring.md, and \
|
||||
(d) record the rationale in a per-iter journal."
|
||||
);
|
||||
}
|
||||
@@ -1,46 +1,17 @@
|
||||
//! pd.2 (`prelude-decouple` milestone): pin the canonical identity of
|
||||
//! the parsed prelude.
|
||||
//! pd.2/pd.3 (`prelude-decouple` milestone): pin the canonical
|
||||
//! identity of the parsed prelude as a literal hex hash. Updates
|
||||
//! intentionally on prelude-content changes (record the why in the
|
||||
//! per-iter journal); accidental drift trips immediately.
|
||||
//!
|
||||
//! Two tests:
|
||||
//! 1. `prelude_ail_and_json_parse_to_identical_module` — cross-form-
|
||||
//! identity preflight. Asserts that parsing `examples/prelude.ail`
|
||||
//! via `ailang_surface::parse` produces a `Module` with the same
|
||||
//! `module_hash` as deserialising `examples/prelude.ail.json` via
|
||||
//! `serde_json::from_str`. This is the load-bearing assumption
|
||||
//! that justifies pd.3's deletion of the JSON file. pd.3 drops
|
||||
//! this test along with the JSON.
|
||||
//! 2. `prelude_parse_yields_canonical_hash` — long-term anchor.
|
||||
//! Asserts the literal hex hash of the parsed Module. Updates
|
||||
//! intentionally on prelude-content changes (with a JOURNAL entry
|
||||
//! naming why); accidental drift trips immediately.
|
||||
//! pd.2 also carried a `prelude_ail_and_json_parse_to_identical_module`
|
||||
//! cross-form-identity preflight that compared `parse(prelude.ail)`
|
||||
//! against `serde_json::from_str(prelude.ail.json)`. The preflight
|
||||
//! discharged its purpose at pd.2 close (PASSED at hash
|
||||
//! `3abe0d3fa3c11c99`) and was retired in pd.3 alongside the
|
||||
//! `prelude.ail.json` file.
|
||||
|
||||
use ailang_core::workspace::module_hash;
|
||||
use ailang_surface::{parse_prelude, PRELUDE_AIL};
|
||||
|
||||
const PRELUDE_JSON: &str = include_str!("../../../examples/prelude.ail.json");
|
||||
|
||||
#[test]
|
||||
fn prelude_ail_and_json_parse_to_identical_module() {
|
||||
// Form A path: parse prelude.ail via ailang_surface::parse.
|
||||
let from_ail = parse_prelude();
|
||||
|
||||
// Form B path: deserialise prelude.ail.json via serde_json (the
|
||||
// pre-pd.2 path; matches what `ailang_core::workspace::load_prelude`
|
||||
// did before retirement).
|
||||
let from_json: ailang_core::ast::Module = serde_json::from_str(PRELUDE_JSON)
|
||||
.expect("examples/prelude.ail.json must deserialise");
|
||||
|
||||
let h_ail = module_hash(&from_ail);
|
||||
let h_json = module_hash(&from_json);
|
||||
|
||||
assert_eq!(
|
||||
h_ail, h_json,
|
||||
"cross-form-identity preflight failed:\n parse(prelude.ail) hash: {}\n deserialize(prelude.ail.json) hash: {}\n\
|
||||
The two parse paths produce structurally different `Module` values for the same prelude content. \
|
||||
pd.3 must NOT delete prelude.ail.json until this is investigated.",
|
||||
h_ail, h_json,
|
||||
);
|
||||
}
|
||||
use ailang_surface::parse_prelude;
|
||||
|
||||
#[test]
|
||||
fn prelude_parse_yields_canonical_hash() {
|
||||
@@ -55,6 +26,3 @@ fn prelude_parse_yields_canonical_hash() {
|
||||
hex below + record the why in the per-iter journal."
|
||||
);
|
||||
}
|
||||
|
||||
// PRELUDE_AIL re-exported for any future test that wants the raw bytes.
|
||||
const _: &str = PRELUDE_AIL;
|
||||
|
||||
@@ -0,0 +1,209 @@
|
||||
# iter pd.3 — prelude.ail.json deleted; prelude-decouple milestone closed
|
||||
|
||||
**Date:** 2026-05-14
|
||||
**Started from:** c0dd2bc334d490e5e90ad10501c33e48076ff2bd
|
||||
**Status:** DONE
|
||||
**Tasks completed:** 8 of 8
|
||||
|
||||
## Summary
|
||||
|
||||
pd.3 closes the **prelude-decouple milestone** (pd.1 + pd.2 + pd.3) by
|
||||
deleting `examples/prelude.ail.json` from the working tree, sweeping the
|
||||
three remaining `include_str!` consumers (one in-mod test in
|
||||
`workspace.rs`, the cross-form-identity preflight in
|
||||
`prelude_module_hash_pin.rs`, the defensive include in `ail/src/main.rs`'s
|
||||
`migrate-canonical-types` subcommand) and the lockstep skip-branch the
|
||||
defensive include necessitated, dropping the §C4 (b) entry from
|
||||
`carve_out_inventory.rs` (8 → 7 named carve-outs), retiring §C4 (b) of
|
||||
`docs/specs/2026-05-13-form-a-default-authoring.md` in place with a
|
||||
"Status: RETIRED 2026-05-14" marker that preserves the original framing
|
||||
as historical context, and adding `prelude_decouple_carve_out_pin.rs`
|
||||
as the milestone's permanent guard against regression. At milestone
|
||||
close: the prelude exists on disk only as `examples/prelude.ail`; core
|
||||
embeds zero prelude bytes; the `migrate-canonical-types` subcommand no
|
||||
longer auto-injects a synthetic prelude entry; the doctrine "authors
|
||||
write `.ail`; the build derives the JSON-AST in-process via
|
||||
`ailang_surface::parse`" (CLAUDE.md) holds without exception. Test count:
|
||||
573 green workspace-wide (preserved from pd.2; +1 carve-out pin -1
|
||||
cross-form-identity preflight +1 relocated `build_workspace_accepts...`
|
||||
test -1 in-mod twin = net 0). `bench/cross_lang.py` + `bench/compile_check.py`
|
||||
exit 0; `bench/check.py` exit 1 with the same envelope-noise pattern
|
||||
observed since audit-cma (14th consecutive observation; pd.3 is a
|
||||
file-system + spec-text + test deletion + dead-code cleanup with zero
|
||||
codegen / runtime / typecheck touch, so cannot be the source).
|
||||
|
||||
## Per-task notes
|
||||
|
||||
- iter pd.3.1: `build_workspace_accepts_assembled_modules_and_runs_validation`
|
||||
RELOCATED from `crates/ailang-core/src/workspace.rs::tests` (in-mod) to
|
||||
`crates/ailang-core/tests/workspace_pin.rs` (integration crate) instead
|
||||
of the literal call-site swap the plan prescribed. The plan specified
|
||||
"use the fully-qualified `ailang_surface::parse_prelude()` at the call
|
||||
site" claiming it was the workaround pd.2 used; in fact pd.2 worked
|
||||
around this same dev-dep cycle by inlining
|
||||
`serde_json::from_str(include_str!("prelude.ail.json"))` precisely
|
||||
BECAUSE `parse_prelude()` doesn't typecheck from in-mod (the lib-test
|
||||
target sees two distinct `ailang-core` compilations and the `Module`
|
||||
types fail to unify). pd.3 cannot use the JSON workaround because the
|
||||
JSON file is being deleted. The structural fix is the relocation
|
||||
pattern pd.2.4 already used for 10 sibling tests — same crate edge,
|
||||
same outcome. A 6-line stub-comment in `workspace.rs::tests` documents
|
||||
the dev-dep cycle so future maintainers don't try to put the test
|
||||
back. New test `load_one_adapter` helper at the top of `workspace_pin.rs`
|
||||
mirrors the in-mod `load_one` exactly (Io/Schema mapping). DONE_WITH_CONCERNS:
|
||||
plan defect (treated under pd.2.4 lesson recommendation but not
|
||||
caught at planning time).
|
||||
- iter pd.3.2: 5 deletions in `prelude_module_hash_pin.rs` (cross-form-
|
||||
identity preflight test fn, `PRELUDE_JSON` const, `PRELUDE_AIL` import,
|
||||
`const _: &str = PRELUDE_AIL;` shape-pin, lines 1-15 of original
|
||||
module doc-comment); module doc-comment replaced verbatim with the
|
||||
surviving-only version (preserves the historical record of why the
|
||||
preflight existed, what it discharged, when it was retired). Hash
|
||||
`3abe0d3fa3c11c99` still asserted by the surviving
|
||||
`prelude_parse_yields_canonical_hash`. Zero orphan-import warnings.
|
||||
First-shot clean.
|
||||
- iter pd.3.3: defensive `include_str!` block (12 lines) deleted from
|
||||
`Cmd::MigrateCanonicalTypes` arm; lockstep skip-branch (4 lines)
|
||||
deleted from the rewrite loop's Pass 2. The plan referenced the
|
||||
subcommand by its variant tag `MigrateBareCrossModuleRefs` but the
|
||||
user-facing subcommand name is `migrate-canonical-types` — spot-test
|
||||
ran under the correct name and the help text printed. DONE_WITH_CONCERNS:
|
||||
plan referenced obsolete variant name; doc-comment update on
|
||||
`MigrateCanonicalTypes` (one paragraph) was needed beyond strict task
|
||||
text because the prelude-fallback capability that the doc-comment
|
||||
asserted ("Prelude is consulted as an implicit last-resort fallback
|
||||
when no listed import owns the name") was silently removed by Step 2
|
||||
— leaving the doc-comment in would have been an active mis-statement
|
||||
in `--help` output. Update names pd.3 as the retirement point.
|
||||
- iter pd.3.4: 3 edits to `carve_out_inventory.rs` (header sentence
|
||||
"eight" → "seven"; comment block re-published per plan; `"prelude.ail.json"`
|
||||
entry + its `// §C4 (b) — compile-time-embed` comment dropped from
|
||||
EXPECTED). Test RED-on-purpose post-edit (file still on disk);
|
||||
flips GREEN at Task 7.
|
||||
- iter pd.3.5: Status marker inserted verbatim above §C4 (b) heading
|
||||
in `docs/specs/2026-05-13-form-a-default-authoring.md`. Original §C4
|
||||
(b) text preserved as historical context (per the spec's convention
|
||||
for retired sections). Reads coherently top-to-bottom; the marker
|
||||
reads as a forward-pointer to the resolution and the original text
|
||||
reads as historical motivation.
|
||||
- iter pd.3.6: `crates/ailang-surface/tests/prelude_decouple_carve_out_pin.rs`
|
||||
created verbatim per the plan. RED-on-purpose at this task (file still
|
||||
exists); flips GREEN at Task 7. Failure message names the four things
|
||||
a future maintainer must do to legitimately reintroduce the file —
|
||||
pre-empts the "it's just one file" rationalisation.
|
||||
- iter pd.3.7: pre-deletion verification clean (zero `include_str!`
|
||||
consumers; the two non-`include_str!` `.rs` references are inert —
|
||||
`print_no_leak_pin.rs:110` is a string-literal in an assertion
|
||||
message, `prelude_injection_pin.rs:28` constructs a tempdir-relative
|
||||
path that does not read the in-tree file). `rm examples/prelude.ail.json`.
|
||||
Both pin tests flip RED → GREEN at this step. Workspace test count
|
||||
unchanged at 573 green.
|
||||
- iter pd.3.8: `bench/compile_check.py` exit 0; `bench/cross_lang.py`
|
||||
exit 0; `bench/check.py` exit 1 with the established noise envelope
|
||||
(the `bench_list_sum.bump_s` and `implicit_at_rc` tail-latency
|
||||
oscillation observed since audit-cma — 14th consecutive observation).
|
||||
Acceptance criterion sweep: zero `include_str!` of `prelude.ail.json`
|
||||
workspace-wide; zero literal-`"prelude"` in `crates/ailang-core/src/`
|
||||
production scope (the matches in `workspace.rs` are all inside
|
||||
`#[cfg(test)] mod tests`); CLAUDE.md "Authors write `.ail`" sentence
|
||||
is consistent with the post-milestone state without modification (the
|
||||
prelude exception it implicitly tolerated pre-milestone is gone);
|
||||
`docs/DESIGN.md` carries no production-doctrine assertions about core
|
||||
embedding the prelude or referencing `prelude.ail.json`.
|
||||
|
||||
## Concerns
|
||||
|
||||
- pd.3.1: plan defect — the task text instructed a call-site swap
|
||||
("In each affected test fn body, replace the pattern ... with
|
||||
`ailang_surface::parse_prelude()`") that doesn't typecheck due to
|
||||
the dev-dep cycle. The plan also misattributed pd.2's actual
|
||||
workaround (`serde_json::from_str(include_str!(...))`) as
|
||||
successful use of `parse_prelude()` from in-mod. Recommend planner
|
||||
recon-check: when a plan proposes a call-site change involving
|
||||
`ailang_surface::*` from in-mod tests, verify the test target can
|
||||
resolve the dev-dep edge (the lib-test target generally cannot;
|
||||
the integration-test crate generally can — same lesson pd.2.4
|
||||
recorded but didn't propagate to the planner skill).
|
||||
- pd.3.3: plan referenced subcommand by its internal variant tag
|
||||
(`MigrateBareCrossModuleRefs`) instead of its user-facing name
|
||||
(`migrate-canonical-types`). Trivial repair (test under correct
|
||||
name) but recommend planner check: verify subcommand-arm references
|
||||
in spot-test commands by enumeration-after-rename.
|
||||
- pd.3.3 (deeper): the plan's "Architecture" preamble called the
|
||||
defensive include "a documented no-op" because the inserted entry
|
||||
was unconditionally skipped by the lockstep guard at lines 500-505.
|
||||
This is correct about WRITING the rewritten module file back, but
|
||||
not about CONSULTING `local_types["prelude"]` for fallback rewrites
|
||||
(which the inserted entry populated, lines 487-497, before the Pass
|
||||
2 skip). Removing both blocks silently retired the prelude-fallback
|
||||
capability of the rewrite logic. The plan called the change net-zero
|
||||
behaviour; it isn't. The doc-comment update I made on
|
||||
`MigrateCanonicalTypes` makes the new behaviour visible. The
|
||||
rewrite-helper's prelude-fallback branch at lines ~2436-2446 in
|
||||
`main.rs` is now dead-by-construction (the `local_types` map will
|
||||
never have a `"prelude"` key, since no prelude module is loaded by
|
||||
this subcommand) — left in place per the implementer's
|
||||
no-surrounding-cleanup discipline; tagged below as known debt.
|
||||
|
||||
## Known debt
|
||||
|
||||
- `crates/ail/src/main.rs:~2436-2446`: the `local_types.get("prelude")`
|
||||
fallback in `rewrite_def`'s `rewrite_type_con_owner` (or analogue)
|
||||
is dead-by-construction post-pd.3 since the `local_types` map for
|
||||
the `migrate-canonical-types` subcommand no longer contains a
|
||||
`"prelude"` key. Out of pd.3's literal scope (no surrounding cleanup);
|
||||
candidate for a follow-up tidy iter or for the post-pd.3 audit's
|
||||
drift sweep.
|
||||
- 3 cargo doc warnings on dangling `[`load_workspace`]` references in
|
||||
`Workspace` and `Registry` rustdoc — pre-existing baseline carried
|
||||
forward from pd.2; out of pd.3 scope. (Identical entry was in the
|
||||
pd.2 known-debt section.)
|
||||
- The `migrate-canonical-types` subcommand's substantive behaviour
|
||||
(rewriting bare cross-module refs across a directory of `.ail.json`
|
||||
files) hasn't been exercised since the canonical-type-names
|
||||
milestone. No in-tree fixtures known to need rewriting today; spot-
|
||||
tested via `--help` only. If a future migration is requested, the
|
||||
prelude-fallback removal + the rewrite-helper dead-branch should
|
||||
be reviewed together.
|
||||
|
||||
## Files touched
|
||||
|
||||
Modified:
|
||||
- `crates/ail/src/main.rs` (delete defensive include 472-484, delete
|
||||
skip-branch 500-505, update `MigrateCanonicalTypes` doc-comment to
|
||||
reflect retired prelude-fallback)
|
||||
- `crates/ailang-core/src/workspace.rs` (delete in-mod
|
||||
`build_workspace_accepts_assembled_modules_and_runs_validation` test;
|
||||
replace with 6-line stub comment documenting the dev-dep cycle as
|
||||
invariant)
|
||||
- `crates/ailang-core/tests/carve_out_inventory.rs` (header sentence
|
||||
+ comment block + EXPECTED list — 8 → 7)
|
||||
- `crates/ailang-core/tests/workspace_pin.rs` (add `load_one_adapter`
|
||||
helper at top; append the relocated test at end)
|
||||
- `crates/ailang-surface/tests/prelude_module_hash_pin.rs` (delete
|
||||
cross-form-identity preflight + supporting `PRELUDE_JSON` /
|
||||
`PRELUDE_AIL` references; trim doc-comment to surviving-only
|
||||
framing)
|
||||
- `docs/specs/2026-05-13-form-a-default-authoring.md` (insert "Status:
|
||||
RETIRED" marker above §C4 (b) heading)
|
||||
|
||||
New:
|
||||
- `crates/ailang-surface/tests/prelude_decouple_carve_out_pin.rs`
|
||||
(single test asserting `examples/prelude.ail.json` does not exist)
|
||||
|
||||
Deleted:
|
||||
- `examples/prelude.ail.json` (the milestone's primary artefact
|
||||
retirement)
|
||||
|
||||
## Stats
|
||||
|
||||
`bench/orchestrator-stats/2026-05-14-iter-pd.3.json`
|
||||
|
||||
## Note on pre-existing dirty INDEX.md
|
||||
|
||||
When this iter dispatched, `docs/journals/INDEX.md` was already
|
||||
modified in the working tree with one Boss-side line for pd.2 (the
|
||||
prior iter); that edit was not committed before pd.3 dispatch. This
|
||||
agent did NOT touch INDEX.md (it is Boss-only per skill discipline).
|
||||
The Boss should commit the pd.2 INDEX line either with the pd.3
|
||||
commit or as a separate prior commit — both options are clean.
|
||||
@@ -67,3 +67,5 @@
|
||||
- 2026-05-14 — iter rpe.1.tidy: subst.rs preserves Type::Fn modes through rebuild → 2026-05-14-iter-rpe.1.tidy.md
|
||||
- 2026-05-14 — iter cli-diag-human: WorkspaceLoadError thru diagnostic in non-JSON CLI path → 2026-05-14-iter-cli-diag-human.md
|
||||
- 2026-05-14 — iter pd.1: core API split — load_modules_with + build_workspace + implicit_imports threading; load_workspace_with kept as 3-line shim so surface unchanged; production literal-"prelude" count in workspace.rs dropped 8→4 → 2026-05-14-iter-pd.1.md
|
||||
- 2026-05-14 — iter pd.2: surface owns prelude embed (PRELUDE_AIL + parse_prelude in ailang-surface); pd.1 shim load_workspace_with retired along with PRELUDE_JSON / load_prelude / load_one; cross-form-identity preflight PASSED at module_hash 3abe0d3fa3c11c99; production literal-"prelude" in core/src/ now zero → 2026-05-14-iter-pd.2.md
|
||||
- 2026-05-14 — iter pd.3: prelude.ail.json deleted from working tree; cross-form-identity preflight + supporting bytes deleted from prelude_module_hash_pin (purpose discharged); migrate-bare-cross-module-refs defensive include + lockstep skip-branch deleted; carve_out_inventory.rs §C4(b) row dropped (8→7); form-a-default-authoring spec §C4(b) RETIRED status marker added; new prelude_decouple_carve_out_pin.rs asserts JSON file does not exist; milestone prelude-decouple closed → 2026-05-14-iter-pd.3.md
|
||||
|
||||
@@ -267,6 +267,15 @@ Justification per file:
|
||||
(changing the diagnostic) or refuse to encode at all (changing
|
||||
the test subject).
|
||||
|
||||
> **Status: RETIRED 2026-05-14 by milestone prelude-decouple.** The
|
||||
> §C4 (b) compile-time-embed carve-out is empty post that milestone;
|
||||
> the prelude is now authored exclusively as `examples/prelude.ail`
|
||||
> and parsed at compile time via `ailang_surface::parse_prelude`. The
|
||||
> original §C4 (b) framing below is preserved as historical context;
|
||||
> it documents the doctrine inconsistency that prelude-decouple
|
||||
> resolved. See `docs/specs/2026-05-14-prelude-decouple.md` and the
|
||||
> per-iter journals 2026-05-14-iter-pd.{1,2,3}.md for the resolution.
|
||||
|
||||
**(b) Compile-time-embed carve-outs (one file as of 2026-05-13):**
|
||||
|
||||
```
|
||||
|
||||
@@ -1,559 +0,0 @@
|
||||
{
|
||||
"schema": "ailang/v0",
|
||||
"name": "prelude",
|
||||
"imports": [],
|
||||
"defs": [
|
||||
{
|
||||
"kind": "type",
|
||||
"name": "Ordering",
|
||||
"vars": [],
|
||||
"doc": "Result of a three-way comparison: LT (less than), EQ (equal), GT (greater than). Ships in milestone 23 as the codomain of Ord.compare.",
|
||||
"ctors": [
|
||||
{ "name": "LT", "fields": [] },
|
||||
{ "name": "EQ", "fields": [] },
|
||||
{ "name": "GT", "fields": [] }
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "class",
|
||||
"name": "Eq",
|
||||
"param": "a",
|
||||
"doc": "Structural equality. Ships in milestone 23 alongside Ord. The primitive `==` operator stays as the surface-level comparator this iter; routing `==` through `Eq.eq` is the declared P2 follow-up (see docs/specs/2026-05-10-23-eq-ord-prelude.md, `Out of scope`).",
|
||||
"methods": [
|
||||
{
|
||||
"name": "eq",
|
||||
"type": {
|
||||
"k": "fn",
|
||||
"params": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"param_modes": ["borrow", "borrow"],
|
||||
"ret": { "k": "con", "name": "Bool" },
|
||||
"effects": []
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Eq",
|
||||
"type": { "k": "con", "name": "Int" },
|
||||
"doc": "Eq Int. Body lowers to `icmp eq i64` via the existing primitive `==` dispatch in `lower_eq`.",
|
||||
"methods": [
|
||||
{
|
||||
"name": "eq",
|
||||
"body": {
|
||||
"t": "lam",
|
||||
"params": ["x", "y"],
|
||||
"paramTypes": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"retType": { "k": "con", "name": "Bool" },
|
||||
"body": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "==" },
|
||||
"args": [
|
||||
{ "t": "var", "name": "x" },
|
||||
{ "t": "var", "name": "y" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Eq",
|
||||
"type": { "k": "con", "name": "Bool" },
|
||||
"doc": "Eq Bool. Body lowers to `icmp eq i1` via the existing primitive `==` dispatch in `lower_eq`.",
|
||||
"methods": [
|
||||
{
|
||||
"name": "eq",
|
||||
"body": {
|
||||
"t": "lam",
|
||||
"params": ["x", "y"],
|
||||
"paramTypes": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"retType": { "k": "con", "name": "Bool" },
|
||||
"body": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "==" },
|
||||
"args": [
|
||||
{ "t": "var", "name": "x" },
|
||||
{ "t": "var", "name": "y" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Eq",
|
||||
"type": { "k": "con", "name": "Str" },
|
||||
"doc": "Eq Str. The lambda body shape mirrors Int/Bool for round-trip stability, but the codegen intercept in `emit_fn` overrides the body to call `@ail_str_eq` directly — see `try_emit_primitive_instance_body` in `crates/ailang-codegen/src/lib.rs`.",
|
||||
"methods": [
|
||||
{
|
||||
"name": "eq",
|
||||
"body": {
|
||||
"t": "lam",
|
||||
"params": ["x", "y"],
|
||||
"paramTypes": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"retType": { "k": "con", "name": "Bool" },
|
||||
"body": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "==" },
|
||||
"args": [
|
||||
{ "t": "var", "name": "x" },
|
||||
{ "t": "var", "name": "y" }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "class",
|
||||
"name": "Ord",
|
||||
"param": "a",
|
||||
"superclass": { "class": "Eq", "type": "a" },
|
||||
"doc": "Total ordering. Ships in milestone 23 alongside Eq. `compare x y` returns LT, EQ, or GT (the three-ctor Ordering ADT also in the prelude). Decision 11's single-superclass closure requires `instance Eq T` for every `instance Ord T` — the three Ord instances below pair with the three Eq instances shipped in iter 23.2.3.",
|
||||
"methods": [
|
||||
{
|
||||
"name": "compare",
|
||||
"type": {
|
||||
"k": "fn",
|
||||
"params": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"param_modes": ["borrow", "borrow"],
|
||||
"ret": { "k": "con", "name": "Ordering" },
|
||||
"effects": []
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Ord",
|
||||
"type": { "k": "con", "name": "Int" },
|
||||
"doc": "Ord Int. The lambda body shape is a placeholder for round-trip stability; the codegen intercept `try_emit_primitive_instance_body::\"compare__Int\"` emits a three-way `icmp slt` / `icmp eq` branch ladder constructing LT / EQ / GT.",
|
||||
"methods": [
|
||||
{
|
||||
"name": "compare",
|
||||
"body": {
|
||||
"t": "lam",
|
||||
"params": ["x", "y"],
|
||||
"paramTypes": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"retType": { "k": "con", "name": "Ordering" },
|
||||
"body": {
|
||||
"t": "ctor",
|
||||
"type": "Ordering",
|
||||
"ctor": "EQ",
|
||||
"args": []
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Ord",
|
||||
"type": { "k": "con", "name": "Bool" },
|
||||
"doc": "Ord Bool. Body lowered via `try_emit_primitive_instance_body::\"compare__Bool\"` — `icmp ult i1` LT-test, `icmp eq i1` EQ-test, GT default.",
|
||||
"methods": [
|
||||
{
|
||||
"name": "compare",
|
||||
"body": {
|
||||
"t": "lam",
|
||||
"params": ["x", "y"],
|
||||
"paramTypes": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"retType": { "k": "con", "name": "Ordering" },
|
||||
"body": {
|
||||
"t": "ctor",
|
||||
"type": "Ordering",
|
||||
"ctor": "EQ",
|
||||
"args": []
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Ord",
|
||||
"type": { "k": "con", "name": "Str" },
|
||||
"doc": "Ord Str. Body lowered via `try_emit_primitive_instance_body::\"compare__Str\"` — `call i32 @ail_str_compare(ptr, ptr)` then branch on slt-0 / eq-0 against the normalised {-1, 0, +1} return.",
|
||||
"methods": [
|
||||
{
|
||||
"name": "compare",
|
||||
"body": {
|
||||
"t": "lam",
|
||||
"params": ["x", "y"],
|
||||
"paramTypes": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"retType": { "k": "con", "name": "Ordering" },
|
||||
"body": {
|
||||
"t": "ctor",
|
||||
"type": "Ordering",
|
||||
"ctor": "EQ",
|
||||
"args": []
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "class",
|
||||
"name": "Show",
|
||||
"param": "a",
|
||||
"doc": "Producer of a human-readable Str representation. Ships in milestone 24 with primitive instances for Int/Bool/Str/Float; user types declare their own instance.",
|
||||
"methods": [
|
||||
{
|
||||
"name": "show",
|
||||
"type": {
|
||||
"k": "fn",
|
||||
"params": [{ "k": "var", "name": "a" }],
|
||||
"param_modes": ["borrow"],
|
||||
"ret": { "k": "con", "name": "Str" },
|
||||
"ret_mode": "own",
|
||||
"effects": []
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Show",
|
||||
"type": { "k": "con", "name": "Int" },
|
||||
"methods": [
|
||||
{
|
||||
"name": "show",
|
||||
"body": {
|
||||
"t": "lam",
|
||||
"params": ["x"],
|
||||
"paramTypes": [{ "k": "con", "name": "Int" }],
|
||||
"retType": { "k": "con", "name": "Str" },
|
||||
"body": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "int_to_str" },
|
||||
"args": [{ "t": "var", "name": "x" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Show",
|
||||
"type": { "k": "con", "name": "Bool" },
|
||||
"methods": [
|
||||
{
|
||||
"name": "show",
|
||||
"body": {
|
||||
"t": "lam",
|
||||
"params": ["x"],
|
||||
"paramTypes": [{ "k": "con", "name": "Bool" }],
|
||||
"retType": { "k": "con", "name": "Str" },
|
||||
"body": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "bool_to_str" },
|
||||
"args": [{ "t": "var", "name": "x" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Show",
|
||||
"type": { "k": "con", "name": "Str" },
|
||||
"methods": [
|
||||
{
|
||||
"name": "show",
|
||||
"body": {
|
||||
"t": "lam",
|
||||
"params": ["x"],
|
||||
"paramTypes": [{ "k": "con", "name": "Str" }],
|
||||
"retType": { "k": "con", "name": "Str" },
|
||||
"body": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "str_clone" },
|
||||
"args": [{ "t": "var", "name": "x" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "instance",
|
||||
"class": "Show",
|
||||
"type": { "k": "con", "name": "Float" },
|
||||
"methods": [
|
||||
{
|
||||
"name": "show",
|
||||
"body": {
|
||||
"t": "lam",
|
||||
"params": ["x"],
|
||||
"paramTypes": [{ "k": "con", "name": "Float" }],
|
||||
"retType": { "k": "con", "name": "Str" },
|
||||
"body": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "float_to_str" },
|
||||
"args": [{ "t": "var", "name": "x" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "ne",
|
||||
"doc": "Polymorphic disequality. `ne x y` ≡ not (eq x y). Ships in milestone 23 as the Eq-class free helper.",
|
||||
"type": {
|
||||
"k": "forall",
|
||||
"vars": ["a"],
|
||||
"constraints": [{ "class": "Eq", "type": { "k": "var", "name": "a" } }],
|
||||
"body": {
|
||||
"k": "fn",
|
||||
"params": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"param_modes": ["borrow", "borrow"],
|
||||
"ret": { "k": "con", "name": "Bool" },
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
"params": ["x", "y"],
|
||||
"body": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "not" },
|
||||
"args": [{
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "eq" },
|
||||
"args": [
|
||||
{ "t": "var", "name": "x" },
|
||||
{ "t": "var", "name": "y" }
|
||||
]
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "lt",
|
||||
"doc": "Polymorphic strict-less-than. `lt x y` ≡ case compare x y of LT -> True; _ -> False. Ships in milestone 23 as the Ord-class free helper.",
|
||||
"type": {
|
||||
"k": "forall",
|
||||
"vars": ["a"],
|
||||
"constraints": [{ "class": "Ord", "type": { "k": "var", "name": "a" } }],
|
||||
"body": {
|
||||
"k": "fn",
|
||||
"params": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"param_modes": ["borrow", "borrow"],
|
||||
"ret": { "k": "con", "name": "Bool" },
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
"params": ["x", "y"],
|
||||
"body": {
|
||||
"t": "match",
|
||||
"scrutinee": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "compare" },
|
||||
"args": [
|
||||
{ "t": "var", "name": "x" },
|
||||
{ "t": "var", "name": "y" }
|
||||
]
|
||||
},
|
||||
"arms": [
|
||||
{
|
||||
"pat": { "p": "ctor", "ctor": "LT", "fields": [] },
|
||||
"body": { "t": "lit", "lit": { "kind": "bool", "value": true } }
|
||||
},
|
||||
{
|
||||
"pat": { "p": "wild" },
|
||||
"body": { "t": "lit", "lit": { "kind": "bool", "value": false } }
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "le",
|
||||
"doc": "Polymorphic less-than-or-equal. `le x y` ≡ case compare x y of GT -> False; _ -> True. Ships in milestone 23 as the Ord-class free helper.",
|
||||
"type": {
|
||||
"k": "forall",
|
||||
"vars": ["a"],
|
||||
"constraints": [{ "class": "Ord", "type": { "k": "var", "name": "a" } }],
|
||||
"body": {
|
||||
"k": "fn",
|
||||
"params": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"param_modes": ["borrow", "borrow"],
|
||||
"ret": { "k": "con", "name": "Bool" },
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
"params": ["x", "y"],
|
||||
"body": {
|
||||
"t": "match",
|
||||
"scrutinee": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "compare" },
|
||||
"args": [
|
||||
{ "t": "var", "name": "x" },
|
||||
{ "t": "var", "name": "y" }
|
||||
]
|
||||
},
|
||||
"arms": [
|
||||
{
|
||||
"pat": { "p": "ctor", "ctor": "GT", "fields": [] },
|
||||
"body": { "t": "lit", "lit": { "kind": "bool", "value": false } }
|
||||
},
|
||||
{
|
||||
"pat": { "p": "wild" },
|
||||
"body": { "t": "lit", "lit": { "kind": "bool", "value": true } }
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "gt",
|
||||
"doc": "Polymorphic strict-greater-than. `gt x y` ≡ case compare x y of GT -> True; _ -> False. Ships in milestone 23 as the Ord-class free helper.",
|
||||
"type": {
|
||||
"k": "forall",
|
||||
"vars": ["a"],
|
||||
"constraints": [{ "class": "Ord", "type": { "k": "var", "name": "a" } }],
|
||||
"body": {
|
||||
"k": "fn",
|
||||
"params": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"param_modes": ["borrow", "borrow"],
|
||||
"ret": { "k": "con", "name": "Bool" },
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
"params": ["x", "y"],
|
||||
"body": {
|
||||
"t": "match",
|
||||
"scrutinee": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "compare" },
|
||||
"args": [
|
||||
{ "t": "var", "name": "x" },
|
||||
{ "t": "var", "name": "y" }
|
||||
]
|
||||
},
|
||||
"arms": [
|
||||
{
|
||||
"pat": { "p": "ctor", "ctor": "GT", "fields": [] },
|
||||
"body": { "t": "lit", "lit": { "kind": "bool", "value": true } }
|
||||
},
|
||||
{
|
||||
"pat": { "p": "wild" },
|
||||
"body": { "t": "lit", "lit": { "kind": "bool", "value": false } }
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "ge",
|
||||
"doc": "Polymorphic greater-than-or-equal. `ge x y` ≡ case compare x y of LT -> False; _ -> True. Ships in milestone 23 as the Ord-class free helper.",
|
||||
"type": {
|
||||
"k": "forall",
|
||||
"vars": ["a"],
|
||||
"constraints": [{ "class": "Ord", "type": { "k": "var", "name": "a" } }],
|
||||
"body": {
|
||||
"k": "fn",
|
||||
"params": [
|
||||
{ "k": "var", "name": "a" },
|
||||
{ "k": "var", "name": "a" }
|
||||
],
|
||||
"param_modes": ["borrow", "borrow"],
|
||||
"ret": { "k": "con", "name": "Bool" },
|
||||
"effects": []
|
||||
}
|
||||
},
|
||||
"params": ["x", "y"],
|
||||
"body": {
|
||||
"t": "match",
|
||||
"scrutinee": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "compare" },
|
||||
"args": [
|
||||
{ "t": "var", "name": "x" },
|
||||
{ "t": "var", "name": "y" }
|
||||
]
|
||||
},
|
||||
"arms": [
|
||||
{
|
||||
"pat": { "p": "ctor", "ctor": "LT", "fields": [] },
|
||||
"body": { "t": "lit", "lit": { "kind": "bool", "value": false } }
|
||||
},
|
||||
{
|
||||
"pat": { "p": "wild" },
|
||||
"body": { "t": "lit", "lit": { "kind": "bool", "value": true } }
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"kind": "fn",
|
||||
"name": "print",
|
||||
"doc": "Polymorphic console-print helper. `print x` ≡ `do io/print_str (show x)` with an explicit let-binder around `show x` for heap-Str RC discipline per eob.1 Str carve-out. Ships in milestone 24 as the second half of the Show prelude.",
|
||||
"type": {
|
||||
"k": "forall",
|
||||
"vars": ["a"],
|
||||
"constraints": [{ "class": "Show", "type": { "k": "var", "name": "a" } }],
|
||||
"body": {
|
||||
"k": "fn",
|
||||
"params": [{ "k": "var", "name": "a" }],
|
||||
"param_modes": ["borrow"],
|
||||
"ret": { "k": "con", "name": "Unit" },
|
||||
"effects": ["IO"]
|
||||
}
|
||||
},
|
||||
"params": ["x"],
|
||||
"body": {
|
||||
"t": "let",
|
||||
"name": "s",
|
||||
"value": {
|
||||
"t": "app",
|
||||
"fn": { "t": "var", "name": "show" },
|
||||
"args": [{ "t": "var", "name": "x" }]
|
||||
},
|
||||
"body": {
|
||||
"t": "do",
|
||||
"op": "io/print_str",
|
||||
"args": [{ "t": "var", "name": "s" }]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user