{"atom":"regression/for-expression-is-a-list","family":"regression","slug":"for-expression-is-a-list","header":{"atom":"regression/for-expression-is-a-list","kind":"whole-program — a minimal repro, not yet reduced to an atom","source":"curated"},"axes":null,"cols":null,"engines":["rakudo-2026.07","rakupp-3.14.0"],"kinds":["ref","pp"],"defFrom":"rakupp:t/regression/for-expression-is-a-list.raku","counts":{"a":1,"f":0,"d":0,"c":0,"r":0,"n":0},"files":["atoms/regression/for-expression-is-a-list.grid"],"records":[["0001","","# Regression: a `for` used as an EXPRESSION returned a different type depending\n# on how it was spelled.\n#\n#     my \\a = ($_ for ^2);      # was Seq,  Rakudo says List\n#     my $b = (for ^5 { $_ });  # was List, Rakudo says List  <- already right\n#\n# Raku++ disagreeing with ITSELF is what marked this as a bug rather than a\n# deliberate laziness choice. The cause was not in the loop at all: the parser\n# desugars `(EXPR for LIST)` to `map({ EXPR }, LIST)`, and `map` correctly gives\n# a Seq — while the block spelling goes through ForStmt's asExpr path, which\n# builds a List. The desugaring now ends in `.List`.\n#\n# Nothing was lost by making it a List: the desugaring is eager either way\n# (`is-lazy` was already False on both spellings and both engines), so the Seq\n# tag was only ever a name. Raku++ also caps an infinite source at 10,000\n# iterations where Rakudo hangs — a separate, pre-existing difference this does\n# not touch.\n#\n# It surfaced on 2026-08-10 when `eqv` learned to tell a List from a Seq\n# (t/regression/eqv-list-vs-array.raku): S04-statements/for.t's two\n# \"assigning list comprehension to sigilless works\" assertions started failing,\n# having passed only because eqv ignored the container type.\n#\n# Checked against Rakudo, and this file is meant to pass under both — run\n# `raku t/regression/for-expression-is-a-list.raku` as the oracle.\n#\n# Contract: exit 0 + last line PASS.\nmy @fail;\n\nsub check($got, $want, $desc) {\n    @fail.push(\"$desc: got {$got.raku}, want {$want.raku}\") unless $got eqv $want;\n}\n\n# --- the two spellings must agree, and both must be List ---------------------\ncheck ($_ for ^2).WHAT.^name,       'List', 'a statement-modifier for is a List';\ncheck (for ^2 { $_ }).WHAT.^name,   'List', 'a block for is a List';\ncheck ($_ * 2 for 1..3).WHAT.^name, 'List', 'a modifier for with an expression body';\n\n# --- and the values are right, which is what Roast asserts -------------------\ncheck (($_ for ^1) eqv (0,)),       True, 'a one-element comprehension eqv (0,)';\ncheck (($_ for ^2) eqv (0, 1)),     True, 'a two-element comprehension eqv (0, 1)';\ncheck (($_ * 2 for 1..3) eqv (2, 4, 6)), True, 'a doubling comprehension';\ncheck ((for ^3 { $_ }) eqv (0, 1, 2)),   True, 'the block spelling eqv a List too';\n\n# --- neither spelling is lazy, which is why the tag was safe to change -------\ncheck ($_ for ^2).is-lazy,     False, 'a modifier for is eager';\ncheck (for ^2 { $_ }).is-lazy, False, 'a block for is eager';\n\n# --- the source type does not change the answer ------------------------------\nmy @src = 1, 2;\ncheck ($_ for @src).WHAT.^name,      'List', 'over an Array';\ncheck ($_ for (^2).Seq).WHAT.^name,  'List', 'over a Seq';\ncheck ($_ for 1..2).WHAT.^name,      'List', 'over a Range';\n\n# --- SIDE EFFECTS and topicalisation must be untouched by the retag ----------\n{\n    my @out;\n    (@out.push($_) for ^3);\n    check @out.List, (0, 1, 2), 'the body still runs, in order';\n}\n{\n    my $n = 0;\n    ($n++ for ^4);\n    check $n, 4, 'the body still runs the right number of times';\n}\n{\n    # $_ is restored afterwards\n    $_ = 'outer';\n    my $ignored = ($_ for ^2);   # bound, not sunk: Rakudo warns about `$_` in sink context\n    check $_, 'outer', 'the topic is restored after the loop';\n    check $ignored.elems, 2, '…and the loop still produced its values';\n}\n\n# --- the contextualiser forms that ride on this ------------------------------\ncheck @($_ * 2 for 1..3).Array, [2, 4, 6], 'an @(...) contextualiser over a modifier for';\ncheck (($_ for ^2),).elems,     1,          'a comprehension nests as one item';\n\nif @fail {\n    note \"FAILED:\\n\" ~ @fail.map({ \"  - $_\" }).join(\"\\n\");\n    exit 1;\n}\nsay \"PASS\";","output PASS","a",["PASS","PASS"],0,[["budget","20s"]]]]}