# Copyright (c) 2012, Limin Fu
# This document is released under the GNU Free Documentation License.

load help;

load help_dao_type_number;
load help_dao_type_enum;
load help_dao_type_string;
load help_dao_type_array;
load help_dao_type_list;
load help_dao_type_map;
load help_dao_type_tuple;

@[name]
dao.type
@[name]

@[title]
Data Type
@[title]

@[author]
Limin Fu
@[author]

@[license]
GNU Free Documentation License
@[license]

@[text]
Dao supports optional/hybrid typing that allows variables to be declared
with or without type names. When a variable is declared without type name,
its type will be automatically inferred at compiling time if possible.
If a variable has an explicitly declared type or a successfully inferred type,
static type checking will be carried out on this variable at compiling time,
otherwise, it will be checked at running time.


The most basic inference is based on assignment,
so the simplest way to declare a variable to have certain type without explicitly
specifying it is by assigning a value of the desired type to the variable at 
the declaration, for example @[code]id = 0@[code] will decalare @[code]id@[code]
as an integer, which is equivalent to @[code]id :int = 0@[code].


@[section]Explicit Type Declaration@[section]

The type of a variable can be declared explicitly in the following form:
@[code]
variable : type
variable : type = value
@[code]
The same form can also be used in function parameter list:

@[code]
routine MyFunction( name : string, index : int = 1 ) { ... }
@[code]


Multiple variables can be declared once with the same type and initial value:
@[code]
variable1, variable2 : type
variable1, variable2 : type = value
@[code]


@[section]Outline of Dao Types@[section]

Dao supports a rich set of types to allow writing expressive codes.


@[subsection]Basic Types@[subsection]
The basic types are the following:
@[list]
--@[green]int@[green] : integer type;
--@[green]float@[green] : single precision floating point number;
--@[green]double@[green] : double precision floating point number;
--@[green]complex@[green] : complex number (double precision for each part);
--@[green]long@[green]   : arbitrary precision integer;
--@[green]string@[green] : string type;
--@[green]enum@[green] : enum type;
@[list]
Please see @[green]dao.type.typename@[green] for details.


@[subsection]Builtin Aggregated Types@[subsection]
Dao has builtin support for some essential aggregated types including:
@[list]
--@[green]array@[green] : multi-dimensional numeric array;
--@[green]tuple@[green] : tuple type;
--@[green]list@[green] : list container;
--@[green]map@[green] : map or hash map container;
@[list]
Please see @[green]dao.type.typename@[green] for details.


@[subsection]Other Types@[subsection]
Dao also supports a number of other types such as:
@[list]
--Routine or function types (see @[green]dao.routine@[green] for details);

--Wrapped C/C++ types (see @[green]dao.c-interface@[green] for details);

--User defined Dao class types (see @[green]dao.class@[green] for details);

--Abstract interface types (see @[green]dao.???@[green] for details);

--@[green]stream@[green] type for standard Input/Output and file Input/Output
(see @[green]dao.type.stream@[green] for details);

--Types for concurrent programming such @[green]future@[green], @[green]mutex@[green],
@[green]condition@[green] and @[green]semaphore@[green] 
(see @[green]dao.concurrent@[green] for details);

--Exception types (essentially wrapped C types).
@[list]


@[subsection]Special Types@[subsection]

@[list]
--@[green]any@[green]:

As its name suggests, it represents any type, so any value can match to this type.


--@[green]?@[green]:

This special type name represents undefined types. Function parameters that 
have neither type annotations nor default values are automatically assigned with
this type. Some variables whose types cannot be successfully inferred are also
assigned with this type. This type can be specialized to a more specific type
when more type information is available, for example, at function call point 
(compile time or runtime), parameters passed to a function call can provide 
concrete types to specialize the function (namely, specializing parameter types, 
local variable types and virtual machine instructions).


--Type holder:

Type holders are type names with a @[green]@@[green] prefix.
It also represents to-be-defined types, the same type holder represents the same type
in a local context (a single composite type or a function prototype etc).
For example,

@[code]
tuple<@T,@T>
@[code]
represents a tuple type whose two items must have the same type.

@[code]
routine Func( a :@T, b :@T )
@[code]
declares a function that can only accept two parameters of the same type.


--Value type:

Constant values of basic types can also be used as types in a composite type.
For example,

@[code]
tuple<1,2>
@[code]
represents a tuple type whose first item must be @[green]1@[green] and its second
must be @[green]2@[green]!
Such types are mainly useful when dealing with C++ template types.


@[subsection]Type Aliasing@[subsection]

Type alias can be defined using the following syntax:

@[code]
#TypeAliasing ::= 'type' Identifier '=' Type
type alias = another_type
@[code]
For example,
@[code]
type StringList = list<string>
type Point3D    = tuple<x:float,y:float,z:float>
@[code]

@[text]



@[name]
dao.type.variant
@[name]

@[title]
Variant or Disjoint Union Type
@[title]

@[text]

A variant type is a type that can represent multiple alternative types.
A variant type is declared by joining these alternative types using 
@[green]|@[green] as the delimiter.
A variable of a variant type can hold value of any of the alternative types.
For example,
@[code]
intstr : int|string = 123
intstr = 'abc'
@[code]
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 @[green]?<@[green], which will return 1-based
index of the real type in the list of alternative types:
@[code]
switch( intstr ?< type(int|string) ){
case 1 : io.writeln( (int) intstr * 1000 )
case 2 : io.writeln( (string) intstr + 'abcdefg' )
}
@[code]


@[code]
intstring : list<int|string> = {};

intstring.append( 123 );
intstring.append( 'abc' );

#intstring.append( {} ); # typing error

io.writeln( intstring, intstring[0], intstring[1] );
@[code]




@[code]
interface HasSizeMethod
{
	routine Size()=>int;
}
class AA
{
	routine Size()=>int{ return 10 }
}
class BB
{
	routine Size()=>int{ return 20 }
}

routine Test( object : AA | BB | HasSizeMethod )
{
# casting to an interface will invoke automatic binding:
	object2 = (HasSizeMethod) object;
	io.writeln( object2.Size() )
}

io.writeln( std.about( Test ) );

Test( AA() )
Test( BB() )

routine Test2( data : int | float | double | string )
{
# get 1-based index of its type in the disjoint union type:
	switch( data ?< type(int|float|double|string) ){
	case 1 : io.writeln( 'handling int' );
	case 2 : io.writeln( 'handling float' );
	case 3 : io.writeln( 'handling double' );
	case 4 : io.writeln( 'handling string' );
	}
}

Test2( 1 );
Test2( 1.0 );
Test2( 1.0D );
Test2( 'abc' );
@[code]




@[code]
class FakeImage
{
	var image = [1,2,3,4;5,6,7,8;11,12,13,14;15,16,17,18];

# instead of writing operator methods with all the combinations
# such as tuple<int,int>, tuple<int,none>, ...
# one can use disjoint union to simplify this.
	operator[]( i : int, js : tuple<int|none,int|none> )=>array<int>
	{
# one can simply return image[i,js], but the following is for demonstration purpose:
		j1 = 0;
		j2 = image.dim(1) - 1;
		if( js[0] != none ) j1 = (int) js[0];
		if( js[1] != none ) j2 = (int) js[1];
		return image[i,j1:j2];
	}
}

image = FakeImage();
io.writeln( image[1,1:] );
io.writeln( image[2,:1] );
@[code]




@[code]
routine Sum( alist : list<@T<int|string>> ) => @T
{
#	reflect.trace();
	return alist.sum();
}

s = Sum( { 1, 2, 3 } );
#s += 'a'; # typing error
io.writeln( s );

s2 = Sum( { 'a', 'b', 'c' } );
io.writeln( s2 );
@[code]
@[text]

