E08277: the same variable is read by 'cat' and appended to ('>>' or 'tee') in one stream statement, so the read never reaches the end - input is output; read a copy instead, use '>' to replace it, or collect into a new variable

Category: E08xxx Pre-IR checks (flow analysis) · Reported in phase: PRE_IR_CHECKS · Classification: STREAM_APPENDS_TO_ITS_SOURCE · All error codes

What went wrong

One stream statement reads a variable with 'cat' and appends to that same variable - with '>>' as its termination, or with a 'tee' stage that has it as its target. It does not matter what the variable is: a List, a Set, a Dict, a PriorityQueue, a JSON - there is one rule and no list of types. The stream pulls from the variable one element at a time while the append keeps adding to it, so the read can never reach the end of what it is reading. GNU cat says the same of a file: "input file is output file". The compiler reports it on the target, by the variable itself and not by its name, so it also finds a source and a target written as 'this.items' and plain 'items' (one property, two spellings), and 'cat first, items >> items' with the variable among several sources.

//'>>' appends to 'scores' while 'cat' is still reading it, so the read can never reach the end.
cat scores >> scores

Only '>>' and 'tee' are reported. A '>' fills a fresh collection and swaps it in when the stream is finished, so 'cat items > items' is well defined and is not an error. And a variable held by another object ('other.items') is another variable, even though the property has the same name.

A 'tee' counts at any stage of the pipeline, however long, with a mapper or without, and so does every source of a 'cat a, b, c'. The expression form ('x <- cat items | tee in items | collect as ...') is judged the same way.

Where the compiler cannot see two names for one value - 'alias <- items' then 'cat items >> alias', or one List handed to a function as both its source and its target - the program runs, and the stream stops, before it reads anything, with an Exception whose reason is "<Type> changed while it was being read" ("List changed while it was being read", "Set ...", "Dict ...", "PriorityQueue ...", "JSON ..."): the statement compares the objects it reads with the objects it appends to.

Worked examples

Compilable examples demonstrating this error and the correct fix:

  • ek9 -q 2250 (Why does 'cat items >> items' not compile, and how do I append a List to itself?)

Each of these is fine:

  • //Read a copy: the stream reads 'copy' while '>>' appends to 'scores'.
  • copy <- List() of Integer
  • copy += scores
  • cat copy >> scores
  • //Or replace: '>' fills a fresh List and swaps it in when the stream is finished.
  • cat scores | sort > scores
  • //Or build the result in a new variable.
  • sorted <- cat scores | sort | collect as List of Integer

How to fix it

  1. Read a copy: build a new collection of the same kind, append the original to it, and let 'cat' read the copy while '>>' appends to the original.
  2. Or replace the List with '>' - the result is built aside and swapped in when the stream is finished, so the stream can read what the List held.
  3. Or build the result in a new variable ('collect as List of ...') and keep the original as it was.
  4. If the report is on a 'tee', give the 'tee' a different List from the one 'cat' reads.

Common causes

  • Writing the Unix idiom 'cat file >> file' to double or extend a List, a Set, a Dict, a PriorityQueue or a JSON
  • Collecting the filtered elements back into the List they were filtered from, with '>>' instead of '>'
  • A 'tee' that records what passes through into the very collection being read

See also

  • E08276: another stream-source check, a for-range that can never move toward its end

Examples

Every example is compiled by the EK9 compiler: the first fails exactly as captioned, the others fix it.

Fails with E08277: the same variable is read by 'cat' and appended to ('>>' or 'tee') in one stream statement, so the read never reaches the end - input is output; read a copy instead, use '>' to replace it, or collect into a new variable

#!ek9
defines module error.e08277.bad

  defines function

    appendToItself()
      stdout <- Stdout()
      scores <- [3, 1, 2]
      //'>>' appends to 'scores' while 'cat' is still reading it, so the read can never reach the end.
      cat scores >> scores
      stdout.println($scores)

Fixed: Read a copy of the List, replace it with '>', or collect into a new variable

#!ek9
defines module error.e08277.good

  defines function

    appendWithoutReadingWhatIsAppendedTo()
      stdout <- Stdout()
      scores <- [3, 1, 2]
      //Read a copy: the stream reads 'copy' while '>>' appends to 'scores'.
      copy <- List() of Integer
      copy += scores
      cat copy >> scores

      //Or replace: '>' fills a fresh List and swaps it in when the stream is finished.
      cat scores | sort > scores

      //Or build the result in a new variable.
      sorted <- cat scores | sort | collect as List of Integer
      stdout.println($sorted)

Questions and answers

These Q&A entries show E08277 and its fix: