experiment(cma): familiar-vs-unfamiliar — AILang is not harder for a frontier model given a scan (refs #68 #69 #70)
Follows up the SMA controls with the sharper question: does AILang cost a frontier model MORE than a language it knows from training (Rust)? Claude passing SMA is binary and cannot tell "effortless" from "barely". Measured with pass@1 over 6 samples of the same task in AILang (two-example few-shot) vs Rust, single-shot, fresh clueless agents, no tools, oracled afterward. Task 1 (Expr-evaluator — ADT + match + recursion, all in the few-shot): AILang 6/6 = Rust 6/6, no gap; all six AILang answers byte-identical. When every construct is in the few-shot, AILang is not harder. Task 2 (count-greater-than — needs a comparison, OUTSIDE the few-shot): AILang 0/6 vs Rust 6/6. But the logic was correct in all six; they fell on a guessed name (`>`, which AILang lacks). A vocabulary gap, not a reasoning gap — and the single-shot setup is unfair: in Rust Claude implicitly has its training plus the obvious ability to scan an unknown crate, which AILang was denied. The fair test (scan + compiler-loop, how Claude actually works): one Claude context, count-greater-than, given the prelude as a scanned library, then iterated on raw compiler output. Converges in two feedback rounds — gt+match-on-Bool -> (case true ..) -> match (compare h N) .. GT -> green. Decisive contrast: no-scan + terse errors -> Claude DIVERGES (invents gtPos, sub, non-existent pat-var); scan + diagnostic errors -> Claude CONVERGES. Same model; the difference is the discovery affordances, not the model. The 0/6 was the artefact of an unfair test, not an intrinsic AILang weakness — with the scan-plus-iterate workflow, the "language is harder" gap dissolves. Two genuine AILang defects the thread exposed, filed as issues: - #69: `ail builtins` is an incomplete API scan — it lists no comparison operator; gt/lt/le/ge/compare live in the prelude, which builtins does not surface. A model scanning the obvious discovery tool never finds half the comparison stdlib. - #70: `match` on Bool passes `ail check` but codegen rejects it (`match on non-ADT scrutinee (i1); MVP supports only ADTs`); with no `if`, branching on a Bool has no working surface — one must route through compare->Ordering. A check/codegen inconsistency under an "internal:" prefix. Answer, decomposed: cognitively AILang is not harder (the algorithm transfers); vocabulary-without-scan is harder but that is any unfamiliar language; with scan + iteration Claude drives AILang like a foreign crate; where it genuinely is harder is the two fixable tooling/compiler defects above. Caveat: n=6, two small tasks, one model, low sampling variance — exploratory, not a study. Evidence under experiments/2026-05-12-cross-model-authoring/familiar-vs-unfamiliar/.
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# Is AILang harder for a frontier model than a familiar language?
|
||||
|
||||
Evidence corpus for the "Is AILang harder for a frontier model…" section of
|
||||
`../format-findings.md`. The question: does AILang cost Claude *more* than a
|
||||
language it knows from training (Rust)? Method: pass@1 over 6 samples of the
|
||||
same task in AILang (two-example few-shot) vs Rust, single-shot, fresh clueless
|
||||
agents, no tools, oracled afterward (`ail check`/`run` resp. `rustc` + stdout).
|
||||
|
||||
## task1-eval — ADT + match + recursion (all in the few-shot)
|
||||
|
||||
Evaluate `(A + B) * C` over a small `Expr` ADT. **AILang 6/6 = Rust 6/6**, no
|
||||
gap — and all six AILang answers were byte-identical. When every construct is in
|
||||
the few-shot, AILang is not harder for Claude. `ail/m.ail` runs to `20`.
|
||||
|
||||
## task2-countgt — count elements `> N` (needs a comparison, OUTSIDE the few-shot)
|
||||
|
||||
- `ail-noscan/m.ail` — the single-shot answer. The *logic is correct* (recurse,
|
||||
test, accumulate); it fails only on a guessed name: `> ` is not an AILang
|
||||
identifier. Representative of **0/6** in this condition. The Rust cell
|
||||
(`rust.rs`, idiomatic `filter().count()`) is **6/6**. So the single-shot gap
|
||||
is a *vocabulary* gap, not a reasoning gap — and the single-shot setup is
|
||||
unfair: in Rust Claude has its training plus the obvious ability to scan a
|
||||
crate, which AILang was denied.
|
||||
|
||||
- `ail-scan-loop/m.ail` — the **fair test**: same task, but Claude is given the
|
||||
prelude as a scanned library and then iterates on raw compiler output (how it
|
||||
actually works on an unfamiliar crate). It converges in two feedback rounds:
|
||||
`gt`+`(pat-ctor True)` → `(case true …)` → finally `match (compare h N) … GT`.
|
||||
Runs to `3`. The idiomatic path matches on the `Ordering` ADT that `compare`
|
||||
returns — never on a `Bool`.
|
||||
|
||||
## The finding
|
||||
|
||||
*No scan + terse errors* → Claude diverges. *Scan + diagnostic errors* → Claude
|
||||
converges. Same model; the difference is the discovery affordances. The
|
||||
"language is harder" gap dissolves under the workflow Claude really uses.
|
||||
|
||||
Two genuine AILang defects the thread exposed (filed as Gitea issues):
|
||||
|
||||
1. `ail builtins` lists no comparison operator — they live in the prelude, which
|
||||
`builtins` doesn't surface. The discovery tool is an incomplete API scan.
|
||||
2. `match` on `Bool` passes `ail check` but is rejected by codegen
|
||||
(`match on non-ADT scrutinee (i1); MVP supports only ADTs`); with no `if`,
|
||||
branching on a Bool is a dead end. A check/codegen inconsistency.
|
||||
Reference in New Issue
Block a user