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.

Category: Enquiry · Compare: IntelliJ 'Analyze Data Flow' + inspection hints (advisory, not an auto-fix)

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

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

ArgumentTypeRequiredWhat it is
filestringyesThe EK9 source file path
lineinteger1-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.
symbolstringA 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.