Universe-Topic-Slang-Plan
Universe / Topic Slang β Implementation Plan
RakuAST slang that makes caller topic ($_) an explicit operand for
universe-based set operators (and optionally bare navigation), eliminating
nqp::ctxcaller / getlexrel digs.
Related: Raku/problem-solving#524
(callsite $_ injection β we do not wait on a core will ast trait;
we use a Qwiratry slang instead).
Goals
Universe ops work under
given(and any lexical$_), including insideTestsubtestblocks.No runtime topic dig for those ops (
universe-from-caller/navigation-topicviactxcallerretired for this path).RakuAST only β legacy grammar unsupported for Qwiratry.
query-ast { }still returns Qwiratry operator nodes with the universe captured as a real AST operand (not resolved only at eval time via NQP).Prefer a Slangify Grammar + Actions approach (same family as Mold slang).
Non-goals for the first landing (historical; Phase 3 bare-nav also landed):
Supporting non-RakuAST Rakudo builds.
Lizmatβs proposed core
will asttrait.One-
useTopic activation (blocked by infix categoricals / MAIN refresh).
Problem (short)
Universe operators (parens β© β β‘ β) and bare nav prefixes used to call
navigation-topic(nqp::ctxcaller(β¦)). After 6.d, $_ is not dynamic; givenβs
topic is often invisible to NQP lexpad walks from a callee. Explicit
$_ = $universe works; given often does not. Fixed by Topic slang +
explicit $universe / bare-nav helpers (no dig).
Target semantics
Universe infixes / prefix
| Form today | Meaning | Target lowering |
|---|---|---|
@A β @B with topic $_ = U | U β (A β© B) | Call / apply with third operand = lexical $_ (name $universe in the op sub if easy; otherwise the injected AST node is still $_) |
@A β© @B, β, β‘ | same pattern | same |
β @A | U β A | Unary becomes binary in the op API: universe + collection, or keep prefix but Actions wrap as a call with $_ first |
Preferred runtime signature sketch:
multi sub infix:<β>(Mu $left, Mu $right, Mu $universe = $*QWIRATRY-UNIVERSE) { ... }
# Actions always pass $universe explicitly as lexical $_ from the callsite AST.
# Default optional only as a safety net during migration; remove dig entirely.If a three-arg infix is awkward in RakuAST ApplyInfix (binary-only), Actions emit an
explicit call to &infix:<β> (or a named helper &qwiratry-universe-nand)
with three args: $left, $right, $_.
Bare navigation (Phase B)
| Form | Target |
|---|---|
βͺͺ $sel (prefix) | Lower to infix/call with LHS = lexical $_ |
$node βͺͺ $sel | Unchanged |
Architecture
Landed load order (two uses β one-use activation is not viable on current Rakudo):
use Qwiratry::Query::Slang; # re-exports Ops (operators)
use Qwiratry::Query::Slang::Topic; # Slangify TopicGrammar/TopicActions β AFTER Ops
β
βΌ
Importer CU parse β Actions rewrite PREFIX/INFIX / given
β
βΌ
Rewrite matching universe and bare-nav nodes:
inject RakuAST::Var::Lexical.new('$_') as universe / LHS
β
βΌ
Runtime helpers: no ctxcaller; universe/LHS is a normal argumentWhy two uses: (1) Slangify + many infix is export in the same CU prevents Topic
activation; (2) importing operator categoricals refreshes MAIN and wipes a Topic
mixin applied in the same use. Thin Query::Slang re-exports Ops via
sub EXPORT { Map.new(Ops::EXPORT::DEFAULT.WHO.pairs) }.
Reuse patterns from lib/Qwiratry/Mold/Slang.rakumod:
Topic is a file-level compunit (no
unit module) so Slangify runs in the importer.use v6.e.PREVIEW.RakuAST-only actions (no legacy grammar role).
Mold slang loads Ops then Topic so mold
whenbodies get the same rewrites.
Phases
Phase 0 β Spike (Β½β1 day)
Prove Actions can rewrite one universe form end-to-end.
Minimal Slangify actions that intercept
β(simplest: one operand today).User code:
given @U { say (β @A).List }must work inside asubtest.Confirm injected node is lexical
$_(DEPARSE / AST dump).Confirm
query-ast({ given β¦; β @A })orquery-ast({ $_ = β¦; β @A })still yields aSetDifferenceOperator(or successor) with universe operand represented somehow (literal capture vs topic sentinel β decide in spike).
Exit: spike branch or throwaway script + short note of which AST node types
were rewritten (ApplyPrefix vs Call::Name vs custom).
Phase 1 β Runtime API without dig
Change universe multis to take explicit
$universe(positional 3rd or leading, pick one and stick to it).Remove
universe-from-callerfrom those multis (keepnavigation-topicfor bare nav until Phase B).Temporary bridge: if
$universeundefined, throw a clear error pointing at slang/given/$_ =β do not silently dig.Update unit tests that used
$_ = @Uto keep working; restoregivenonce slang lands.
Phase 2 β Topic slang for all universe glyphs
Ops: β β© β β‘ β.
Grammar/Actions: identify applications of these operators in the importer AST.
Rewrite to pass lexical
$_as$universe.Document: universe must be the topic (
given U { β¦ }or$_ = U; β¦).Tests:
t/spec/Operators/06.1-set-relational.rakutestΒ§6.1.2 withgiven @Unested
subtest+givenquery-astround-trip / planner smoke
Changelog + Operators.md note (RakuAST required; topic = universe).
Phase 3 β Bare navigation (optional follow-up)
Same slang: rewrite bare nav prefixes to supply LHS =
$_.Fix
t/spec/Operators/05-navigation-topic.rakutestundergiven.Retire
navigation-topic/ctxcallerfrom those prefixes.Infix
$x βͺͺ $yunchanged.
Phase 4 β Cleanup
Delete unused NQP topic helpers (
navigation-topic/universe-from-caller/use nqpdig).META6 provides
Query::Slang,::Ops,::Topic; Extending / Operators / README document two-use load order; RakuAST-only.Full
zef teston moar-2026.07+ β Topic acceptance green; remaining FAIL is table FK/catalog /t/operator/ioskip-all (pre-existing vs Topic work).
Design choices (settled)
| Choice | Decision |
|---|---|
| Mechanism | Slangify Grammar + Actions (not core will ast trait; not CHECK-only unless spike shows Actions insufficient) |
| Compiler | RakuAST only |
| Universe parameter | Prefer explicit $universe; inject AST as lexical $_ |
| Binary ApplyInfix limit | If needed, emit call with 3 args instead of ternary ApplyInfix |
| Bare nav | Phase B after universe |
query-ast | Must keep building Qwiratry nodes; universe operand explicit in AST or evaluated arg list per spike |
Open points for the spike
Exact RakuAST node shape for custom ops under 2026.07+ (
ApplyPrefix/ApplyInfix/Call).Whether Actions should mixin and wrap existing
prefix:sym/infix:symmethods, or post-process statement ASTs.How
query-astrepresents βuniverse is topicβ: storeNavQueryTopic-like sentinel vs require topic to be resolvable when the block runs under$*QWIRATRY-BUILD-QUERY.Interaction with Mold slang when both are
used in one CU (activation order).
Test plan (acceptance)
given @U { (@A β© @B).List }insidesubtestβ expected NAND resultSame for
β©ββ‘βquery-ast({ β¦ })still returns non-Seqoperator AST for plannersClear error if universe op used with undefined topic (no silent wrong U)
(Phase B)
given %repo { (βͺͺ <owner>).List }worksFull
zef teston moar-2026.07+ (06.1 + 05-navigation-topic verified; suite still FAIL on pre-existing table FK/catalog,ioskip-all, unrelated)
Landing notes
Ops live in Qwiratry::Query::Slang::Ops. Topic slang is a separate
use Qwiratry::Query::Slang::Topic after Ops/Slang: infix categoricals from
operator import overwrite MAIN if Topic is mixed in the same use. Public entry
Qwiratry::Query::Slang re-exports Ops via sub EXPORT.
Suggested first PR
Phase 0 spike + Phase 1 API change + Phase 2 for β and β only, with
Β§6.1.2 tests restored to given. Remaining glyphs and bare nav in follow-ups.