spec: canonical type names — name MethodNameCollision as the workaround it is
This commit is contained in:
@@ -263,39 +263,54 @@ many ambiguity cases are caught earlier by the validator.
|
|||||||
|
|
||||||
- **Class names** (`ClassDef.name`, `InstanceDef.class`,
|
- **Class names** (`ClassDef.name`, `InstanceDef.class`,
|
||||||
`SuperclassRef.class`, `Constraint.class`): NOT migrated to the
|
`SuperclassRef.class`, `Constraint.class`): NOT migrated to the
|
||||||
module-scoped canonical form in this milestone. The asymmetry to
|
module-scoped canonical form in this milestone — but we name the
|
||||||
Type::Con is intentional and rests on a real semantic difference:
|
reason honestly rather than dressing it as "different semantics".
|
||||||
|
|
||||||
- For types, no workspace-global uniqueness rule exists. Two modules
|
The asymmetry is structural, not semantic. AILang's class-method
|
||||||
may independently declare `type Foo`. Bare cross-module references
|
resolution today (`ModuleGlobals::class_methods: IndexMap<String,
|
||||||
are therefore genuinely ambiguous; the iter-23.3 bug is the
|
ClassMethodEntry>`, `crates/ailang-check/src/lib.rs:983`, consumed
|
||||||
concrete instance of that ambiguity surfacing in unification.
|
by `mono::rewrite_class_method_calls`,
|
||||||
|
`crates/ailang-check/src/mono.rs:657-665`) is **name-driven**: the
|
||||||
|
call `eq x y` is resolved by looking up `"eq"` and getting back
|
||||||
|
exactly one `ClassMethodEntry` carrying its `class_name` and
|
||||||
|
`defining_module`. The class is reverse-inferred from the method.
|
||||||
|
|
||||||
- For classes, workspace-global uniqueness is enforced one level
|
That single-entry guarantee is what `WorkspaceLoadError::MethodNameCollision`
|
||||||
deeper: `WorkspaceLoadError::MethodNameCollision` (iter 22b.2,
|
(iter 22b.2, `workspace.rs:472`) enforces at load time. The
|
||||||
`workspace.rs:472`) rejects two classes that share any method
|
causal chain is:
|
||||||
name. Two `class Eq`s with the conventional `eq` method cannot
|
|
||||||
coexist; `class_by_name: BTreeMap<String, &ClassDef>` (workspace-
|
|
||||||
flat) is consistent with that invariant. Empirically no
|
|
||||||
`examples/` fixture today has a bare cross-module class
|
|
||||||
reference that resolves ambiguously — the cross-module fixtures
|
|
||||||
that exist (`test_22b3_xmod_e2e_main`) call methods bare and rely
|
|
||||||
on the method→class→instance chain, never on a direct class-name
|
|
||||||
reference.
|
|
||||||
|
|
||||||
Three class-reference fields (`InstanceDef.class`,
|
1. Class names are bare in the AST.
|
||||||
`SuperclassRef.class`, `Constraint.class`) gain a *defensive*
|
2. → Class lookup is `BTreeMap<String, &ClassDef>` (workspace-flat,
|
||||||
validator check in ct.1: reject qualified forms (containing `'.'`)
|
bare-keyed).
|
||||||
explicitly, since the schema does not currently model qualified
|
3. → For method resolution to be tractable from a bare `Term::Var
|
||||||
class names. This prevents half-migrated files from silently
|
{ name: "eq" }`, the method name must resolve to a unique class.
|
||||||
loading.
|
4. → `MethodNameCollision` enforces uniqueness as a load-time
|
||||||
|
invariant.
|
||||||
|
|
||||||
If AILang ever revisits workspace-global method-name uniqueness
|
In other words: `MethodNameCollision` is the load-time constraint
|
||||||
(e.g. introduces module-qualified method names so two libraries
|
that makes bare class names viable. Qualifying class names in the
|
||||||
can each declare `class Eq` with its own `eq`), the symmetry to
|
AST would not pay off in isolation — it would force step 3 to be
|
||||||
Type::Con returns and class names will need the same module-scoped
|
replaced with type-driven dispatch (look up `eq` candidates by
|
||||||
treatment. That is a separate DESIGN-level milestone and is not
|
argument type, Haskell-style), which is a Designfeld of its own
|
||||||
this spec's scope.
|
with implications for inference, ambiguity rules, and diagnostic
|
||||||
|
shape. That is out of scope here; this milestone is canonical
|
||||||
|
type names, not a typeclass-dispatch overhaul.
|
||||||
|
|
||||||
|
What this spec DOES do for class-reference fields
|
||||||
|
(`InstanceDef.class`, `SuperclassRef.class`, `Constraint.class`):
|
||||||
|
ct.1's validator rejects *qualified* forms (containing `'.'`) on
|
||||||
|
those fields. The schema does not currently model qualified class
|
||||||
|
names; the rejection prevents half-migrated files from silently
|
||||||
|
loading. `ClassDef.name` itself remains bare (the file-top
|
||||||
|
`"name"` is the module context, same as `TypeDef.name`).
|
||||||
|
|
||||||
|
Forward note: if AILang ever revisits name-driven method
|
||||||
|
dispatch — e.g. to allow two libraries to each declare `class
|
||||||
|
Eq` with its own `eq` — the symmetry returns and class names
|
||||||
|
will then need the same module-scoped treatment, along with
|
||||||
|
type-driven method resolution. That is a separate DESIGN-level
|
||||||
|
milestone and is named here so the workaround is visible rather
|
||||||
|
than buried.
|
||||||
|
|
||||||
- **Surface notation in `.ailx`**: bare local names continue to be
|
- **Surface notation in `.ailx`**: bare local names continue to be
|
||||||
the LLM-author convention. Prose parser/printer bridge — no
|
the LLM-author convention. Prose parser/printer bridge — no
|
||||||
|
|||||||
Reference in New Issue
Block a user