b586999e81
First iteration of the kernel-extension-mechanics milestone. Ships
the type-scoped `<TypeName>.<member>` resolution path as the
canonical form for type-associated operations, narrows the
`BareCrossModuleTypeRef` / `BadCrossModuleTypeRef` diagnostics from
"bare = strictly local" to "bare = in-scope by any path", migrates
12 std-library example fixtures, and introduces a workspace-wide
normalisation pre-pass `prepare_workspace_for_check` shared between
`check_workspace` and `monomorphise_workspace`.
Architectural discovery during implementation: the plan covered the
`Term::Var` dot-qualified resolver layer plus the workspace
validator's bare-name acceptance, but the migration of bare-form
fixtures exposed five sites where bare vs. qualified type-names
needed symmetric treatment — `Term::Ctor` resolution, `Type::Con`
well-formedness, mono's poly-free-fn name/constraint-count
enumeration, codegen's `lookup_ctor_by_type` bare-name path, and
the upstream desugar-then-qualify composition. Rather than
scattering TypeDef-first ladders across each site, the implementer
centralised the work into one pre-pass that walks every consumer
module's `Type::Con.name` and `Term::Ctor.type_name`, rewriting
bare cross-module references to their qualified `<home>.<Type>`
form. This is symmetric to the pre-existing `qualify_local_types`
(owner-side); the new pre-pass is the consumer-side mirror.
Downstream passes see qualified Types regardless of authoring form.
The TypeDef-first ladder still lives in `synth`'s `Term::Var` arm
because `<TypeName>.<member>` is term-position-only — `Maybe.from_maybe`
is a Var, not a Type expression, and the pre-pass does not rewrite
Var names.
Alternatives considered:
(a) Add TypeDef-first ladder at every resolution site separately
(the plan's implicit assumption). Rejected: O(N) extension
sites, each carrying the same workspace-walking logic; the
pre-pass version is O(1) — one pass, every downstream consumer
benefits.
(b) BLOCKED + spec re-brainstorm. Rejected: the architecture
extension is consistent with prep.1's thesis (bare type-name
resolves to the workspace-wide TypeDef) and forward-compatible
with prep.2 (Term::New.type_name falls under the same rewrite)
and prep.3 (kernel-tier TypeDefs enter the workspace map
automatically). No design regression to bounce back over.
Spec updated to document the realisation mechanism honestly: the
"Realisation mechanism — workspace pre-pass" subsection clarifies
that the resolver-level semantics described in "Implementation
shape" are the user-facing contract, and the actual code path is
the pre-pass.
Verification:
- `cargo test --workspace`: ALL GREEN. 87 e2e + every crate's unit
+ integration tests pass with no regressions.
- Three NEW in-source tests pin Task 1's resolver paths:
`type_scoped_member_resolves`, `type_scoped_member_not_found`,
`type_scoped_receiver_not_a_type`.
- One NEW workspace test pins the narrowed validator:
`ct1_validator_accepts_bare_with_explicit_import`.
- One renamed-and-flipped existing test:
`ct1_validator_rejects_bare_xmod_with_import_candidate` →
`ct1_validator_accepts_bare_xmod_with_import_candidate` (the
bare-with-import path is now ACCEPTED).
- One NEW companion test for the workspace-wide ctor lookup:
`ct2_term_ctor_bare_cross_module_via_workspace_resolves`.
- Two pre-existing tests' assertions updated for the new error
wording: `ct1_check_cli::check_human_mode_emits_actionable_message_to_stderr`
and `crates/ailang-check/tests/workspace.rs::unknown_module_prefix_is_reported`.
- 12 migrated `.ail` fixtures verified via the existing e2e
suite (each fixture is the test runner's target for an existing
`build_and_run` assertion).
- Negative fixture `ct_2_bare_cross_module.ail` semantically
preserved: dropped its `(import std_maybe)` so bare `Maybe` is
out-of-scope under the narrowed rule and still fires
`BareCrossModuleTypeRef`.
Concerns:
- The pre-pass introduces a new architectural layer (consumer-side
qualification) that the spec did not originally anticipate. Spec
amendment in this commit documents the layer. Future iterations
reference `prepare_workspace_for_check` as established
infrastructure.
- `examples/test_ct1_bare_xmod_rejected.ail.json` switched its
offending name from bare `Ordering` (which under the prep.1
semantics may now resolve via implicit prelude) to a still-
unresolvable `Mystery_Type`. The CLI test's intent (assert that
a human-mode `ail check` exits non-zero on a still-RED case) is
preserved.
Milestone status: kernel-extension-mechanics (Gitea #6) advances
1/3 iters. Next: prep.2 (`Term::New` construct) issue #32.
197 lines
5.6 KiB
Plaintext
197 lines
5.6 KiB
Plaintext
; Iter 15b — second stdlib module: polymorphic singly-linked lists.
|
|
; Imports std_maybe so head/tail can return Maybe<a> / Maybe<List<a>>.
|
|
; prep.1 migration (kernel-extension-mechanics): bare `Maybe` at
|
|
; type-name slots (in scope via `(import std_maybe)`). Bare ctor
|
|
; names (`Just`, `Nothing`) stay unqualified — once the type is
|
|
; resolved the ctor lookup is unambiguous.
|
|
|
|
(module std_list
|
|
|
|
(import std_maybe)
|
|
|
|
(data List (vars a)
|
|
(doc "Polymorphic singly-linked list: Nil or Cons<a, List<a>>.")
|
|
(ctor Nil)
|
|
(ctor Cons a (con List a)))
|
|
|
|
(fn fold_left
|
|
(doc "Tail-recursive left fold. The recursive call is in tail position and is marked.")
|
|
(type
|
|
(forall (vars a b)
|
|
(fn-type
|
|
(params (fn-type (params b a) (ret b))
|
|
b
|
|
(con List a))
|
|
(ret b))))
|
|
(params f acc xs)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil) acc)
|
|
(case (pat-ctor Cons h t)
|
|
(tail-app fold_left f (app f acc h) t)))))
|
|
|
|
(fn fold_right
|
|
(doc "Right fold. Constructor-blocked: the recursive call is the second arg of f, NOT a tail position.")
|
|
(type
|
|
(forall (vars a b)
|
|
(fn-type
|
|
(params (fn-type (params a b) (ret b))
|
|
b
|
|
(con List a))
|
|
(ret b))))
|
|
(params f acc xs)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil) acc)
|
|
(case (pat-ctor Cons h t)
|
|
(app f h (app fold_right f acc t))))))
|
|
|
|
(fn length
|
|
(doc "Length via fold_left. The accumulating lambda ignores the element and increments the counter.")
|
|
(type
|
|
(forall (vars a)
|
|
(fn-type
|
|
(params (con List a))
|
|
(ret (con Int)))))
|
|
(params xs)
|
|
(body
|
|
(app fold_left
|
|
(lam (params (typed c (con Int)) (typed _ a))
|
|
(ret (con Int))
|
|
(body (app + c 1)))
|
|
0
|
|
xs)))
|
|
|
|
(fn reverse
|
|
(doc "Reverse via fold_left with a flipping accumulator. Tail-recursive by virtue of the fold_left call.")
|
|
(type
|
|
(forall (vars a)
|
|
(fn-type
|
|
(params (con List a))
|
|
(ret (con List a)))))
|
|
(params xs)
|
|
(body
|
|
(app fold_left
|
|
(lam (params (typed acc (con List a)) (typed h a))
|
|
(ret (con List a))
|
|
(body (term-ctor List Cons h acc)))
|
|
(term-ctor List Nil)
|
|
xs)))
|
|
|
|
(fn is_empty
|
|
(doc "True iff the list is Nil.")
|
|
(type
|
|
(forall (vars a)
|
|
(fn-type
|
|
(params (con List a))
|
|
(ret (con Bool)))))
|
|
(params xs)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil) true)
|
|
(case (pat-ctor Cons _ _) false))))
|
|
|
|
(fn head
|
|
(doc "Returns Just<a> of the first element, or Nothing for an empty list. Cross-module Maybe.")
|
|
(type
|
|
(forall (vars a)
|
|
(fn-type
|
|
(params (con List a))
|
|
(ret (con Maybe a)))))
|
|
(params xs)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil) (term-ctor Maybe Nothing))
|
|
(case (pat-ctor Cons h _) (term-ctor Maybe Just h)))))
|
|
|
|
(fn tail
|
|
(doc "Returns Just<List<a>> of the tail, or Nothing for an empty list.")
|
|
(type
|
|
(forall (vars a)
|
|
(fn-type
|
|
(params (con List a))
|
|
(ret (con Maybe (con List a))))))
|
|
(params xs)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil) (term-ctor Maybe Nothing))
|
|
(case (pat-ctor Cons _ t) (term-ctor Maybe Just t)))))
|
|
|
|
(fn append
|
|
(doc "Concatenate two lists. Constructor-blocked recursion: the recursive call is inside Cons, NOT a tail.")
|
|
(type
|
|
(forall (vars a)
|
|
(fn-type
|
|
(params (con List a) (con List a))
|
|
(ret (con List a)))))
|
|
(params xs ys)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil) ys)
|
|
(case (pat-ctor Cons h t)
|
|
(term-ctor List Cons h (app append t ys))))))
|
|
|
|
(fn map
|
|
(doc "Polymorphic map. Constructor-blocked recursion.")
|
|
(type
|
|
(forall (vars a b)
|
|
(fn-type
|
|
(params (fn-type (params a) (ret b))
|
|
(con List a))
|
|
(ret (con List b)))))
|
|
(params f xs)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil) (term-ctor List Nil))
|
|
(case (pat-ctor Cons h t)
|
|
(term-ctor List Cons (app f h) (app map f t))))))
|
|
|
|
(fn filter
|
|
(doc "Keep elements where p is true. Constructor-blocked when p holds; non-tail recursive call when p is false.")
|
|
(type
|
|
(forall (vars a)
|
|
(fn-type
|
|
(params (fn-type (params a) (ret (con Bool)))
|
|
(con List a))
|
|
(ret (con List a)))))
|
|
(params p xs)
|
|
(body
|
|
(match xs
|
|
(case (pat-ctor Nil) (term-ctor List Nil))
|
|
(case (pat-ctor Cons h t)
|
|
(if (app p h)
|
|
(term-ctor List Cons h (app filter p t))
|
|
(app filter p t))))))
|
|
|
|
(fn take
|
|
(doc "First n elements of xs (or all of xs if it has fewer than n).")
|
|
(type
|
|
(forall (vars a)
|
|
(fn-type
|
|
(params (con Int) (con List a))
|
|
(ret (con List a)))))
|
|
(params n xs)
|
|
(body
|
|
(if (app le n 0)
|
|
(term-ctor List Nil)
|
|
(match xs
|
|
(case (pat-ctor Nil) (term-ctor List Nil))
|
|
(case (pat-ctor Cons h t)
|
|
(term-ctor List Cons h (app take (app - n 1) t)))))))
|
|
|
|
(fn drop
|
|
(doc "All elements of xs after the first n (or Nil if xs has fewer than n elements).")
|
|
(type
|
|
(forall (vars a)
|
|
(fn-type
|
|
(params (con Int) (con List a))
|
|
(ret (con List a)))))
|
|
(params n xs)
|
|
(body
|
|
(if (app le n 0)
|
|
xs
|
|
(match xs
|
|
(case (pat-ctor Nil) (term-ctor List Nil))
|
|
(case (pat-ctor Cons _ t)
|
|
(app drop (app - n 1) t)))))))
|