module Methods { 
 
  // INSTANCE METHODS 
 
  // instance methods can be added for a type and then called using a "dot" 
  // syntax e.g. x.f() instead of f(x). 
  // the module defining the method must be imported. 
 
  import { /Std/Math; } 
 
  struct Vector { 
    x : double; 
    y : double; 
    z : double; 
  } 
 
  // the following defines a method for the double type (first parameter) 
  // as a convention the first parameter is shown as this, but can be any name 
  lerp(this : double, to : double, weight : double) : double => { 
    // calculate linear interpolation between this and to given fraction weight 
    return this * (1.0 - f) + (to * weight); 
  } 
 
  fn test1() { 
    let x = 1.0; 
    // the logical equivalent of calling lerp(x, 2.0, 0.5) 
    let y = x.lerp(2.0, 0.5); 
  } 
 
  // METHOD NAME UNIQUENESS 
 
  // method names only need to be unique for the type they apply to 
  // method names must not conflict with field names of the type they apply to 
  // the following defines a lerp method for the Vector type (first parameter) 
  lerp(this : Vector, to : Vector, weight : double) : double => { 
    // apply linear interpolation to a vector 
    return Vector3( 
      this.x.lerp(to.x, weight), 
      this.y.lerp(to.y, weight), 
      this.z.lerp(to.z, weight) 
    ); 
  } 
 
  fn test2() { 
    let v1 = Vector(1, 2, 3); 
    let v2 = Vector(4, 5, 6); 
    let v3 = v1.lerp(v2); 
  } 
 
  // PROPERTIES 
 
  // property getters and setters are defined using .get or .set 
  // note: the variable this could be named anything 
  magnitude.get(this : Vector) : double => { 
    return sqrt( 
      this.x * this.x + 
      this.y * this.y + 
      this.z * this.z 
    ) 
  } 
 
  fn test4() { 
    let v = Vector(1, 2, 3); 
    let m = v.magnitude; 
  } 
 
  // INDEXERS 
 
  // indexers can be defined using get[] or set[]  
  // actual index can be any type (not limited to numbers) 
  get[](this : Vector, index : string) : double => { 
    // return the X, Y, or Z index 
    return match (index) { 
      "x" => this.x; 
      "y" => this.y; 
      "z" => this.z; 
      _ => NaN // handle invalid argument 
    } 
  } 
 
  fn test5() { 
    let v = Vector3(1, 2, 3); 
    let z = v["z"]; 
  } 
 
  // MULTIPLE INDICES 
 
  // A get[]/set[] can have multiple parameters for the indices 
 
  struct Table { 
    width: int; 
    cells : int[]; 
  } 
 
  get[](this : Table, row : int, col : int) : int => { 
    let index = row * width + col; 
    return this.cells[index]; 
  } 
 
  fn test6() { 
    let table = Table(3,[0, 1, 2, 3, 4, 5]); 
    let cell = table.get[1, 0];    // 3 
  } 
 
  // STATIC METHODS 
 
  // static methods are defined for a type by listing the name of the type 
  // followed by the method name. 
  Vector.sum(...vectors: Vector[]) : Vector => { 
    let x = 0.0, y = 0.0, z = 0.0; 
 
    for (v in vectors) { 
      x += v.x; 
      y += v.y; 
      z += v.z; 
    } 
 
    return Vector(x, y, z); 
  } 
 
  fn test7() { 
    let v1 = Vector(1, 2, 3); 
    let v3 = Vector(3, 4, 5); 
    // use the type name followed by the method name to invoke a static method 
    let v3 = Vector.sum(v1, v2); 
  } 
}