    [NAME]
    ALL.dao.type.variant

    [TITLE]
    Variant or Disjoint Union Type

    [DESCRIPTION]

    A variant type is a type that can represent multiple alternative types. A variant type
    is declared by joining these alternative types using | as the delimiter. A variable of a
    variant type can hold value of any of the alternative types. For example,

       1  intstr : int|string = 123
       2  intstr = 'abc'

    Please note that a variant type does not support the operations of the alternative types.
    It must be casted to the real type of the value before using the operations of the real
    type. To find out the real type of the value, one can use the type-of operator ?<, which
    will return 1-based index of the real type in the list of alternative types:

       1  switch( intstr ?< type(int|string) ){
       2  case 1 : io.writeln( (int) intstr * 1000 )
       3  case 2 : io.writeln( (string) intstr + 'abcdefg' )
       4  }

       1  intstring : list<int|string> = {};
       2
       3  intstring.append( 123 );
       4  intstring.append( 'abc' );
       5
       6  #intstring.append( {} ); # typing error
       7
       8  io.writeln( intstring, intstring[0], intstring[1] );

       1  interface HasSizeMethod
       2  {
       3      routine Size()=>int;
       4  }
       5  class AA
       6  {
       7      routine Size()=>int{ return 10 }
       8  }
       9  class BB
      10  {
      11      routine Size()=>int{ return 20 }
      12  }
      13
      14  routine Test( object : AA | BB | HasSizeMethod )
      15  {
      16  # casting to an interface will invoke automatic binding:
      17      object2 = (HasSizeMethod) object;
      18      io.writeln( object2.Size() )
      19  }
      20
      21  io.writeln( std.about( Test ) );
      22
      23  Test( AA() )
      24  Test( BB() )
      25
      26  routine Test2( data : int | float | double | string )
      27  {
      28  # get 1-based index of its type in the disjoint union type:
      29      switch( data ?< type(int|float|double|string) ){
      30      case 1 : io.writeln( 'handling int' );
      31      case 2 : io.writeln( 'handling float' );
      32      case 3 : io.writeln( 'handling double' );
      33      case 4 : io.writeln( 'handling string' );
      34      }
      35  }
      36
      37  Test2( 1 );
      38  Test2( 1.0 );
      39  Test2( 1.0D );
      40  Test2( 'abc' );

       1  class FakeImage
       2  {
       3      var image = [1,2,3,4;5,6,7,8;11,12,13,14;15,16,17,18];
       4
       5  # instead of writing operator methods with all the combinations
       6  # such as tuple<int,int>, tuple<int,none>, ...
       7  # one can use disjoint union to simplify this.
       8      operator[]( i : int, js : tuple<int|none,int|none> )=>array<int>
       9      {
      10  # one can simply return image[i,js], but the following is for demonstration purpose:
      11          j1 = 0;
      12          j2 = image.dim(1) - 1;
      13          if( js[0] != none ) j1 = (int) js[0];
      14          if( js[1] != none ) j2 = (int) js[1];
      15          return image[i,j1:j2];
      16      }
      17  }
      18
      19  image = FakeImage();
      20  io.writeln( image[1,1:] );
      21  io.writeln( image[2,:1] );

       1  routine Sum( alist : list<@T<int|string>> ) => @T
       2  {
       3  #	reflect.trace();
       4      return alist.sum();
       5  }
       6
       7  s = Sum( { 1, 2, 3 } );
       8  #s += 'a'; # typing error
       9  io.writeln( s );
      10
      11  s2 = Sum( { 'a', 'b', 'c' } );
      12  io.writeln( s2 );