Closing iterators

Closing iterators

My presentation to TC39 about an early termination protocol for ES6 iterators, following suggestions made by Jafar Husain at the last face-to-face meeting.

2e36436c692b2e5fbc172e9fb7563171?s=128

dherman

June 05, 2014
Tweet

Transcript

  1. Closing iterators Dave Herman

  2. Iterators need an early-disposal protocol

  3. • Closing a synchronous sequence is a bit of an

    abstract question (though not irrelevant). • But we will want asynchronous sequences, and closing those is definitely important. • We should future-proof for symmetry.
  4. for  (let  x  of  heap.deflateGzipData())  {      ...  

       break;      ...   }
  5. ...   deflateGzipData()  {      let  i  =  this.malloc(...);

         return  {          next()  {  ...  },  //  iterate  typed  array          return()  {  this.free(i)  }      }   }   ...
  6. ...   deflateGzipData:  function*()  {      let  i  =

     this.malloc(...);      try  {          ...  //  iterate  typed  array      }  finally  {          this.free(i)      }   }   ...
  7. for  (await  x  of  db.select(...))  {      ...  

       break;      ...   }
  8. ...   select(query)  {      let  records  =  ...;

         try  {      }  finally  {          records.close();      }   }   ...
  9. When does the early- return get called?

  10. for  (let  x  of  y)  {      ...  

       break;      ...   }
  11. outer:   for  (let  i  =  0;  i  <  N;

     i++)  {      for  (let  x  of  y)  {          ...          break  outer;          ...      }   }
  12. outer:   for  (let  i  =  0;  i  <  N;

     i++)  {      for  (let  x  of  y)  {          ...          continue  outer;          ...      }   }
  13. for  (let  x  of  y)  {      ...  

       throw  new  Error();      ...   }
  14. for  (let  x  of  y)  {      ...  

       f();  //  throws      ...   }
  15. for  (let  x  of  y)  {      ...  

       return;      ...   }
  16. for  (let  x  of  y)  {      ...  

       yield;  //  returns  via  .return()      ...   }
  17. for  (let  x  of  y)  {      ...  

       yield*  g();  //  returns  via  .return()      ...   }
  18. • In short: any abrupt completion of the loop. •

    Normal completion should not call the method; in that case the iterator itself decided to close.
  19. What if the iterator refuses to stop?

  20. function*  f()  {      try  {      

       yield;      }  finally  {  yield;  }   }
  21. • Disallow yield in a finally? • No! Bad idea

    – and doesn't solve the problem.
  22. function*  f()  {      try  {      

       try  {              yield;          }  finally  {  throw  "override";  }      }  catch  (ignore)  {  }      yield;   }
  23. function*  f()  {      try  {      

                     yield*  g();                }  catch  (ignore)  {  }      yield;   }
  24. function*  g()  {      try  {      

       yield;      }  finally  {  throw  "override";  }   }
  25. function*  g()  {      try  {      

       yield;      }  finally  {  cleanup();  }   }
  26. • Disallow yield dynamically, once we start the disposal process?

    • No! Another bad idea, and doesn't solve the problem for hand-written iterators.
  27. • Better framing: for...of gives iterators the opportunity to do

    resource disposal. • Impossible to force an iterator to stop iterating. • Still, failure to stop iterating is probably a bug in the contract between the iterator and the loop.
  28. interface  IterationResult  {      value:  any,      done:

     boolean   }   ! interface  Generator  extends  Iterator  {      next(value:  any?)  :  IterationResult,      throw(value:  any?)  :  IterationResult   ! }
  29. interface  IterationResult  {      value:  any,      done:

     boolean   }   ! interface  Generator  extends  Iterator  {      next(value:  any?)  :  IterationResult,      throw(value:  any?)  :  IterationResult,      return(value:  any?)  :  IterationResult   }
  30. interface  Iterator  {      next(value:  any?)  :  IterationResult,  

       return?()  :  IterationResult   }
  31. • On abrupt exit, for...of looks for return method. •

    If present, it calls the method with no arguments. • If the result has falsy done property, throw an error.
  32. Bikeshed city

  33. interface  Iterator  {      next(value:  any?)  :  IterationResult,  

       close?()  :  IterationResult   }
  34. 5 Jun 14 Resolutions

  35. • Agreed to design, schedule permitting. • Early termination method

    is called return. • If we run out of time, stopgap semantics: • reject yield in try blocks with finally clause • early exit from for...of puts generator in GeneratorComplete state