module Iterators { 
 
  import { /Std; } 
 
  // ITERATOR BASICS 
 
  // Iterators allow steping through a possibly infinite series of values. 
  // 
  // An iterator is a function that returns an optional value 
  // When the function returns none, there are no more values remaining 
  // 
  // typedef Iterator = fn{T}() : T?; 
  // 
  // Types that support iteration implement the Iterable interface. 
  // The iterate method of the Iterable interface returns an Iterator. 
  // 
  // interface Iterable{T} { 
  //   iterate() : Iterator{T}; 
  // } 
  // 
 
  // define Point structure 
  struct Point { 
    x : int; 
    y : int; 
    z : int; 
  } 
 
  // implement Iterable interface for Point 
  impl Iterable{int} { 
 
    // implement iterate method 
    iterate(this : Point) : Iterator{int} => { 
 
      // counter used to track which field to return (xy, or z) 
      let i = 0; 
 
      // return iterator function that returns optional int 
      return fn() : int? => { 
 
        // check counter to determine what to return 
        let result = match (i) { 
          0 => this.x; 
          1 => this.y; 
          2 => this.z; 
          _ => none;     // no more values 
        } 
        if (i < 3) { 
          i += 1; 
        } 
        return result; 
      } 
    } 
  } 
 
  fn test1() { 
    // create a point 
    let p = Point(1, 2, 3); 
 
    // step through values of p 
    for (val in p) { 
      print(val);   // prints 1, 2, 3 
    } 
 
    // iterators can also be called manually 
    // the following is equivalent to the above for loop 
    let iterator = p::Iterable.iterate(); 
    loop { 
      let next = iterator(); 
      if (next) { 
        print(next!); // print value 
      } else { 
        break; // no more values, end loop 
      } 
    } 
  } 
 
  // USING YIELD TO IMPLEMENT ITERATORS 
 
  // use yield to help implement iterators, without having to define functions and maintain state 
  // • use yield{T} as the return type of the iterator function to signal that yield will be used 
  // • use yield to return each value from the iterator 
  // • use yield... to yield all items from an existing iterator 
 
  struct DoorMaterials  { 
    knob : string; 
    frame: string; 
    panel: string; 
    hinges : string[]; 
  } 
 
  // implement Iterable interface for DoorMaterials 
  impl Iterable{string} { 
 
    // implement iterate method using yield 
    iterate(this : DoorMaterials) : yield{string} => { 
 
      // yield each field 
      yield this.knob; 
      yield this.frame; 
      yield this.panel; 
 
      // yield all values in hinges 
      yield ...this.hinges; 
    } 
  } 
 
  fn test2() { 
    let parts = DoorMaterials( 
      knob: "steel", 
      frame: "iron", 
      panel: "wood",  
      hinges: ["brass", "brass", "brass"] 
    ); 
 
    for (part in parts) { 
      print(part);  // prints steelironwoodbrassbrassbrass 
    } 
  } 
}