Slide 1

Slide 1 text

Closing iterators Dave Herman

Slide 2

Slide 2 text

Iterators need an early-disposal protocol

Slide 3

Slide 3 text

• 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.

Slide 4

Slide 4 text

for  (let  x  of  heap.deflateGzipData())  {      ...      break;      ...   }

Slide 5

Slide 5 text

...   deflateGzipData()  {      let  i  =  this.malloc(...);      return  {          next()  {  ...  },  //  iterate  typed  array          return()  {  this.free(i)  }      }   }   ...

Slide 6

Slide 6 text

...   deflateGzipData:  function*()  {      let  i  =  this.malloc(...);      try  {          ...  //  iterate  typed  array      }  finally  {          this.free(i)      }   }   ...

Slide 7

Slide 7 text

for  (await  x  of  db.select(...))  {      ...      break;      ...   }

Slide 8

Slide 8 text

...   select(query)  {      let  records  =  ...;      try  {      }  finally  {          records.close();      }   }   ...

Slide 9

Slide 9 text

When does the early- return get called?

Slide 10

Slide 10 text

for  (let  x  of  y)  {      ...      break;      ...   }

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

• In short: any abrupt completion of the loop. • Normal completion should not call the method; in that case the iterator itself decided to close.

Slide 19

Slide 19 text

What if the iterator refuses to stop?

Slide 20

Slide 20 text

function*  f()  {      try  {          yield;      }  finally  {  yield;  }   }

Slide 21

Slide 21 text

• Disallow yield in a finally? • No! Bad idea – and doesn't solve the problem.

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

function*  g()  {      try  {          yield;      }  finally  {  cleanup();  }   }

Slide 26

Slide 26 text

• Disallow yield dynamically, once we start the disposal process? • No! Another bad idea, and doesn't solve the problem for hand-written iterators.

Slide 27

Slide 27 text

• 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.

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

interface  Iterator  {      next(value:  any?)  :  IterationResult,      return?()  :  IterationResult   }

Slide 31

Slide 31 text

• 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.

Slide 32

Slide 32 text

Bikeshed city

Slide 33

Slide 33 text

interface  Iterator  {      next(value:  any?)  :  IterationResult,      close?()  :  IterationResult   }

Slide 34

Slide 34 text

5 Jun 14 Resolutions

Slide 35

Slide 35 text

• 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