module Unions { 
 
  // UNION BASICS 
 
  // a union type consists of other types.  a union value could be any of those types 
  // but a given value will only be one of those types at a time. 
  // 
  // the values in a union are overlayed in memory.  the union contains an additional 
  // field which references the the type of the current value. 
  // 
  // union types are structural types, meaning two union types with the same set 
  // of types are considered the same type. 
  // 
  // the simplest form of a union type is just a set of types separated by | 
 
  fn test1() { 
    let x : string | int = 1;     // x is int 
    x = "hello";                  // x is string 
 
    // using is or typeof with a union will result in the type of 
    // the current value, not the type of the union itself. 
    print(typeof(x) == ::string); // prints true 
    print(x is string);           // prints true 
 
    // to obtain the value of a union, cast to its type 
    if (x is string) {    // if the union contains a string 
      let s = x::string;  // cast union to string 
      print(s);   // prints hello 
    } 
 
    // match can also be used with unions 
    // see match for more information 
    match (x) { 
      @x : string => print(x);    // prints "hello" 
      @x : int => print(x);       // does not match since x is a string 
    } 
  } 
 
  // NAMED UNIONS 
 
  // unions can also be defined using union, which allows the above syntax 
  // but also additional options (see below).  Named unions define a new 
  // type. 
 
  union CssDimension { 
    int | string 
  } 
 
  // LABELED UNIONS 
 
  // using union allows labeled types.  A labeled type is defined within the union 
  // and it's type is scoped to the union type. 
 
  // define a union with three labeled types 
  union Axis { 
    x : int; 
    y : int; 
    z : int; 
  } 
 
  fn test2() { 
    let a : Axis;   // variable a is of type Axis 
     
    a = Axis(z : 2);    // constructs an Axis.z value 
    a = Axis(y : 2);    // constructs an Axis.y value 
    a = Axis(x : 1);    // constructs an Axis.x value 
 
    // to check if a union u contains a label l, use u.l? 
    // to obtain the value of a label, use u.l 
    if (a.x?) { 
      print(a.x);       // prints 2 
    } 
 
    // match can also be used with labeled unions 
    // see match for more information 
    match (a) { 
      Axis(x : @x) => print(x);   // prints 2 
      Axis(y : @y) => print(y);   // does not match 
      Axis(z : @z) => print(z);   // does not match 
    } 
 
    // match can also be used to match against 
    // the label type, see match for more information 
    match (a) { 
      Axis.x => print("x");   // prints x 
      Axis.y => print("y");   // does not match 
      Axis.z => print("z");   // does not match 
    } 
 
  } 
 
  // DEFINING STRUCTS IN UNIONS 
 
  // As a shortcut, structures can be defined directly within a union 
  // This is equivalent to defining the structure separately 
 
  // For example, the following 
  // 
  //  struct Line { 
  //    length: double; 
  //  } 
  // 
  //  struct Circle { 
  //    radius : double; 
  //  } 
  // 
  //  union Shape { 
  //    Line | Circle 
  //  } 
 
  // could have been written 
 
  union Shape { 
    Line{length : double} 
    Circle{radius : double} 
  } 
 
  // INCLUDING UNIONS 
 
  // To include the members of a union in other union, use ... before the union type being included. 
 
  typedef SignedInt = i8 | i16 | i32 | i64; 
  typedef UnsignedInt = u8 | u16 | u32 | u64; 
  typedef SignedOrUnsignedInt = ...SignedInt | ...UnsignedInt; 
 
  // EXTENSIBLE UNIONS 
 
  // by default, union only accept values from the set of types they define.  to allow other 
  // types to be defined separately from the union, use ... at the end of the union.  
 
  // define a Url union with two types 
  union Url { 
    // define two types  
    Https{host : string; path : string = ""; query : string = ""; fragment : string = ""} 
    Http{host : string; path : string = ""; query : string = ""; fragment : string = ""} 
    ...  // allow other types to be added 
  } 
 
  // add a type  to the Url union 
  union ...Url { 
    File{path : string} 
  } 
 
  // add another type to the Uri union 
  union ...Url { 
    MailTo{email: string; body : string; cc: string} 
  } 
 
  fn test4() { 
    let url : Url; 
    url = Http("https", "google.com"); 
    url = Https("https", "google.com", "/", "", ""); 
    url = MailTo("someone@example.com"); 
  } 
}