E81073: a stream's element type is never closeable - the stages would make resources nothing can close; name each one as a try resource in a 'for' loop ('try -> res <- makeRes(label)') and use it there
What went wrong
A stream stage produces a closeable value - its element type has an 'operator close' - for example 'cat ["a", "b"] | map with makeRes | map with describe', where 'makeRes' returns a 'Res'. A stream's element type is never closeable, whether the closeable comes from a source or from a stage ('map', 'call', 'async', or a 'filter' or 'sort' that only passes it on): it is reported once per stream, at the first place the stream carries one.
A closeable value has exactly one owner, and the compiler must see that owner close it: a try-with-resources resource, a property closed in its type's 'operator close', or the statement that made a value nobody names. A stage in the middle of a pipeline makes one resource per element where there is no try to name it and no statement that made it: the next stage only reads it and the sink only collects it. Nothing ever closed the 'Res' of 'cat ["a", "b"] | map with makeRes | map with describe > stdout', and into a 'List of Base' the stream kept them after it ended, closed or not. This is the stream analogue of E81061, which says a closeable is never a type argument - a stage's element type is not written as one, so it is checked as the stream carries it.
A stage that only READS a closeable it is given is not affected, nor is a stream whose stages produce a String or a number: the closeable is then made, used and closed inside the function, which owns it.
Worked examples
Compilable examples demonstrating this error and the correct fix:
- ek9 -q 1520 (When does EK9 close a closeable value, and where may I keep one?)
How to fix it
Make each value where it is used, in a 'for' loop: name it as a try resource ('try -> res <- makeRes(label)') and use it there - the try closes it when the iteration ends (good_fix.ek9 in the [EXAMPLE] section below).
Or move the work into the function the stage calls: a function that takes the label, makes the resource, reads what it needs and returns a String or a number leaves the stream carrying plain values (good.ek9).
Common causes
- Writing 'map with makeRes' to turn each name into a connection, a file or a handle, and then using the handle in the next stage.
- Calling functions that each return a new resource with 'call' or 'async'.
- Collecting the resources into a list to close them later; a closeable is never a type argument either (E81061).
See also
Examples
Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.
Fails with E81073: a stream's element type is never closeable - the stages would make resources nothing can close; name each one as a try resource in a 'for' loop ('try -> res <- makeRes(label)') and use it there
#!ek9 defines module error.e81073.bad defines class Res name as String? default private Res() Res() -> label as String name: label describe() as pure <- rtn as String: `res ${name}` default operator ? operator close as pure stdout <- Stdout() stdout.println(`closed ${name}`) defines function makeRes() -> label as String <- rtn as Res: Res(label) describeRes() -> res as Res <- rtn as String: res.describe() describeAll() stdout <- Stdout() cat ["a", "b"] | map with makeRes | map with describeRes > stdout
Fixed: Name each resource as a try resource in a for loop instead of mapping to it
#!ek9 defines module error.e81073.good.fix defines class Res name as String? default private Res() Res() -> label as String name: label describe() as pure <- rtn as String: `res ${name}` default operator ? operator close as pure stdout <- Stdout() stdout.println(`closed ${name}`) defines function makeRes() -> label as String <- rtn as Res: Res(label) describeRes() -> res as Res <- rtn as String: res.describe() describeAll() stdout <- Stdout() for label in ["a", "b"] try -> res <- makeRes(label) stdout.println(describeRes(res))
Fixed: Make the resource inside the function the stage calls and return plain text
#!ek9 defines module error.e81073.good defines class Res name as String? default private Res() Res() -> label as String name: label describe() as pure <- rtn as String: `res ${name}` default operator ? operator close as pure stdout <- Stdout() stdout.println(`closed ${name}`) defines function describeLabel() -> label as String <- rtn as String: String() try -> res <- Res(label) rtn: res.describe() describeAll() stdout <- Stdout() cat ["a", "b"] | map with describeLabel > stdout