module Generics { 
 
  // GENERIC BASICS 
 
  // Generic types, functions, and methods allow types to be specified as parameters. 
 
  // The following defines a vector type where the types of the components of the 
  // vector are supplied by the user of the type. 
  struct Vector{T} { 
    x : T; 
    y : T; 
    z : T; 
  } 
 
  fn test1() { 
    let v1 = Vector{float}(1.0, 2.0, 3.0);  // Vector that uses float for it's components 
    let v2 = Vector{double}(1.0, 2.0, 3.0); // Vector that uses double for it's components 
 
    // if the generic is omitted, the compiler will attempt to infer the types based on context. 
    let v3 = Vector(1, 2, 3);           // Vector{int}(1, 2, 3) 
    let v4 = Vector(1.0, 2.0, 3.0);     // Vector{double}(1, 2, 3) 
  } 
 
  // The following method adds two generic Vectors 
  add{T}(this : Vector{T}, other: Vector{T}) : Vector{T} => { 
    return Vector{T}( 
      this.x + other.x, 
      this.y + other.y, 
      this.z + other.z 
    ) 
  } 
 
  fn test2() { 
    let v1 = Vector(1.0, 1.0, 1.0); 
    let v2 = Vector(2.0, 2.0, 2.0); 
 
    // invoke the add method with two vectors, producing a third 
    let v3 = v1.add(v2);    // Vector{double}(3.0, 3.0, 3.0) 
  } 
 
  // INCOMPATIBLE GENERIC TYPES 
 
  // If a generic function is used with a type that is not compatible with it's 
  // implementation, then the compiler will error 
 
  fn test3() { 
    let v1 = Vector("a", "b", "c"); 
    let v2 = Vector("d", "e", "f"); 
 
    // compiler error: add method is not compatible with type Vector{string} 
    let v3 = v1.add(v2); 
  } 
 
  // GENERIC PARAMETER CONSTRAINTS 
 
  // To specify constraints on which types can be used, a boolean expression can be 
  // specified after the generic parameters.  this will cause the errors to be 
  // discovered earlier (when the type is constructed). 
 
  // The boolean expression is a constant expression that includes 
  // the use of ::? (can cast to) 
 
  // define a union type for possible vector coordinate types 
  typedef Coord = float | double | int;  
 
  // the boolean expression after => checks if type T can cast to Coord 
  struct CoordBasedVector{T => T::?Coord} { 
    x : T; 
    y : T; 
    z : T; 
  } 
 
  fn test4() { 
    let v1 = CoordBasedVector{string}("a", "b", "c");  // error: type constraint failed 
  } 
}