[NAME]
ALL.dao.type.variant

[TITLE]
Variant (Disjoint Union) Type

[DESCRIPTION]

A variant type is a type that can represent multiple alternative types. A variant type i
s 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  var intstr : int|string = 123
   2  intstr = 'abc'
     
Please note that a variant type normally does not support the operations of the alternati
ve types. It must be casted to the real type of the value before using the operations of 
the real type. The simplest way to check the value type of a variant type variable is to 
use the type-swith construct: 
     
   1  switch( intstr ) type {
   2  case int    : io.writeln( intstr * 1000 )
   3  case string : io.writeln( intstr + 'abcdefg' )
   4  }
     
Here in each type case, the variable can be used as the corresponding type without explic
it casting.

However, if a variant is compose of only two types and one of them is a none type, the va
riant can be used directly as the other type without explicity casting: 
     
   1  routine Test( maybelist: list<int>|none )
   2  {
   3      if( maybelist == none ) return
   4      maybelist.append( 123 )  # No explicit casting;
   5  }
     


The above mentioned type-switch construct has a couple of variants: 
     
   1  switch( var name = expression ) type {
   2  ...
   3  }
   4  switch( invar name = expression ) type {
   5  ...
   6  }
     
Both will declare a variable name that can only be used inside the switch block. But the 
later one will be declared as an invariable which cannot be modified inside the switch bl
ock.

More examples: 
     
   1  var 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|string )
  27  {
  28      switch( data ) type {
  29      case int    : io.writeln( 'handling int' );
  30      case float  : io.writeln( 'handling float' );
  31      case string : io.writeln( 'handling string' );
  32      }
  33  }
  34  
  35  Test2( 1 );
  36  Test2( 1.0);
  37  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          var j1 = 0;
  12          var j2 = image.dim(1) - 1;
  13          if( js[0] != none ) j1 = js[0];
  14          if( js[1] != none ) j2 = js[1];
  15          return image[i,j1:j2];
  16      }
  17  }
  18  
  19  var 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  var s = Sum( { 1, 2, 3 } );
   8  #s += 'a'; # typing error
   9  io.writeln( s );
  10  
  11  var s2 = Sum( { 'a', 'b', 'c' } );
  12  io.writeln( s2 );