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

Category: E81xxx-E83xxx Test configuration, execution and coverage · Reported in phase: FULL_RESOLUTION · Classification: STREAM_ELEMENT_IS_CLOSEABLE · All error codes

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

  • E81073: A stream's element type is never closeable (this error)
  • E81061: Closeable type as a type argument
  • E81056: Closeable held in a named local (must be a try-with-resources resource)
  • E81072: An unnamed closeable kept by what it is given to

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