[NAME]
ALL.dao.type.variant

[TITLE]
多型(Variant)类型

[DESCRIPTION]

一个多型(Variant或Disjoint Union)类型可用来标注一个变量的多个可能类型。 多型类型的表示由多
个类型名通过|组合起来。 一个多型类型的变量可保存其任意一种可能类型的值。 例如: 
     
   1  var intstr : int|string = 123
   2  intstr = 'abc'
     
值得注意的是多型类型通常不支持它的可能类型的运算操作。它必须作适当的转换 才能用它真实类型的
运算操作。 要检查一个值的类型,并根据不同的类型作不同的操作,最简单的办法是使用 基于类型的
switch-case     
   1  switch( intstr ) type {
   2  case int    : io.writeln( intstr * 1000 )
   3  case string : io.writeln( intstr + 'abcdefg' )
   4  }
     
这里每个类型项的代码块里,变量intstr可直接按相应的类型使用 而不作显式的转换。

不过,当一个多型类型仅有两个类型组成,并且其中之一为空值none类型, 那么这个多型类型可直接作另
一类型使用而不需显式转换。 
     
   1  routine Test( maybelist: list<int>|none )
   2  {
   3      if( maybelist == none ) return
   4      maybelist.append( 123 )  # 用作list<int>,无显式转换;
   5  }
     


上面提到的类型switch有两个变体: 
     
   1  switch( var name = expression ) type {
   2  ...
   3  }
   4  switch( invar name = expression ) type {
   5  ...
   6  }
     
这俩都将声明一个仅在switch块里可用的变量名。 不过后者声明的是一个定变量,可防止该变量在swit
ch块被修改。

更多例子: 
     
   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      var 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.0F );
  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 );