ek9_advise_loop
Answers first: directions[] (each with summary such as 'candidate-stream-aggregation' or 'keep-as-loop', candidateShape, confidence), then the loop{} facts, outputs[], resources[] and unknowns[] - read-only.
When to use it
Should this for/while loop become a stream pipeline, and how - one call with file + line on the loop header (or file + symbol:'<loop variable>'). Any line inside the body also anchors it. Advisory only: the loop is never rewritten.
When not to use it
- The method's overall control-flow structure (branches, guards, dead code) rather than loop-to-stream advice - use
ek9_query_flow. - The body is already a stream pipeline and you want stage-level analysis - use
ek9_query_pipeline. - You just want a type's operator surface (operator |, <=>, #?) with no loop context - use
ek9_query_effective_api. - You have read the advice and are ready to synthesise a stage function / collector operator / record - use
ek9_create_dynamic_function,ek9_add_operator,ek9_new_construct.
Details
Scope is always the single nearest enclosing loop: parse-tree mutation facts always work (PRE_IR_CHECKS, no symbols), while Tier-2 symbol facts (elementType, pipeEligible, bodyCallPurity, readAfterLoop) appear only when the workspace is resolved. pipeEligible mirrors the compiler's own `collect as` verdict — true means `collect as <type>` compiles with zero collector synthesis. directions[] are advisory candidateShapes, not committed edits; a line not on any loop returns an empty loop plus a single 'not-a-loop' direction (not an error); and quiet facts are not proven safety — always read unknowns[] (commutativity, aliasing, iterator single-consumption).
Arguments
| Argument | Type | Required | What it is |
|---|---|---|---|
file | string | yes | The EK9 source file path |
line | integer | 1-based line of the for/while loop header or any line inside the loop body - OMIT it and pass symbol:'<loop variable>' to let the tool find the line. | |
symbol | string | A name on the loop header (the loop variable, e.g. 'i') - the tool finds its line, so you never count lines. |
Recorded calls
Real calls, recorded against small projects and checked on every build - a changed answer fails it.
Advise shape
Request
{
"file" : "Loops.ek9",
"line" : 8
}
Response
{
"directions" : [ {
"candidateShape" : "<result> <- cat 1 ... 10 | <stage>... | collect as Integer",
"confidence" : "medium",
"observation" : "Loop body accumulates [running] from source `1 ... 10`. Element type is `Integer`. `running`'s type `Integer` already implements 'operator |', so `collect as Integer` works directly (no collector synthesis needed).",
"requiresSynthesis" : [ "a dynamic function for any conditional filter/map stage (EK9 has no arrow lambdas)" ],
"suggestedTools" : [ "ek9_create_dynamic_function", "ek9_add_operator", "ek9_new_construct" ],
"summary" : "candidate-stream-aggregation"
}, {
"candidateShape" : null,
"confidence" : "low",
"observation" : "If the accumulation is used once and locally, the imperative loop may read as clearly as a pipeline.",
"requiresSynthesis" : [ ],
"suggestedTools" : [ ],
"summary" : "keep-as-loop"
} ],
"loop" : {
"bodyCallPurity" : "pure",
"containsNestedLoops" : false,
"control" : null,
"elementType" : "Integer",
"headerGuard" : null,
"line" : 8,
"loopKind" : "for-range",
...
Cut short here: 43 more lines. The whole recorded call.