E08056: a guard can skip the stream, but a stream expression has no way to declare the value produced on the skip path; use the statement form with your own accumulator
What went wrong
A stream pipeline whose 'for' source carries a guard is being used as an expression. The guard can skip the whole pipeline, and a stream expression has no way to say what the result should be when that happens.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 1381 (Can I put a guard on a stream 'for' pipeline?)
- ek9 -q 837 (Can I use a guard (including ':=?') inside a switch or ...)
- ek9 -q 89 (How do I use streams and stream pipelines in EK9?)
Why the other expression forms do not have this
Every other guarded expression form declares its own result, and that declaration's initial value IS the result on the skip path - here "no limit":
rtn: switch limit <- maybeLimit() with limit <- result as String: "no limit" case 1 result: "one" default result: "several"
The same shape works for while, do/while, for-in and try. If you omit the initial value there, you get E08050 instead.
A stream expression ends in '| collect as T' and has no returning param, so there is nowhere to write that initial value. The result is whatever T's default constructor produces - and that differs by type in a way the reading of the code does not show. Collected 'as List of Integer', the skip path gives an EMPTY list, which IS set; collected 'as Integer', it gives an UNSET Integer.
How to fix it
- Use the statement form and keep your own accumulator, so you choose the value that survives a skipped guard (the first fix in the [EXAMPLE] section below: a total that starts at 0)
- Or hoist the guard into an enclosing 'if', leaving the stream unguarded:
if limit <- maybeLimit() rtn: for i in 1 ... limit | collect as List of Integer
- Or drop the guard if the pipeline should always run.
Common causes
- Reaching for a stream expression because it is shorter, when the guard means the result is conditional
- Assuming 'collect as' always yields an empty-but-set value; it does for collections, not for scalars
[NOTE ON ':='] A plain ':=' pre-flow is NOT a guard - it always assigns and the body always runs - so it does not raise this error. Only the set-checking pre-flows do: '<-' and ':=?'.
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E08056: a guard can skip the stream, but a stream expression has no way to declare the value produced on the skip path; use the statement form with your own accumulator
#!ek9 defines module error.e08056.bad defines function maybeLimit() as pure <- rtn as Integer: Integer() total() <- rtn as Integer? rtn: for limit <- maybeLimit() then i in 1 ... limit | collect as Integer
Fixed: Use the statement form with your own accumulator
#!ek9 defines module error.e08056.good defines function maybeLimit() as pure <- rtn as Integer: Integer() total() <- rtn as Integer: 0 for limit <- maybeLimit() then i in 1 ... limit rtn += i
Fixed: Or hoist the guard into an 'if'; a guarded switch declares its own result
#!ek9 defines module error.e08056.good.forms defines function maybeLimit() as pure <- rtn as Integer: Integer() hoisted() <- rtn as List of Integer: List() if limit <- maybeLimit() rtn: for i in 1 ... limit | collect as List of Integer described() <- rtn as String? rtn: switch limit <- maybeLimit() with limit <- result as String: "no limit" case 1 result: "one" default result: "several"
Questions and answers
These Q&A entries show E08056 and its fix: