# 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 Types and Type Annotation @[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 annotations. When a variable is declared without type annotation, its type will be automatically inferred at compiling time based on some type dedcution rules if possible. If a variable has an explicit type annotation 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. @[section]Type Annotation@[section] Type annotations in Dao are usually placed after the variable, parameter or field names with a colon separating the names and the annotation. @[code] var name: type var name: type = value routine MyFunction( name: string, index: int = 1 ) { ... } class Klass { var name: type = value } @[code] Multiple variables can be declared at once with the same type and initial value: @[code] var name1, name2 : type var name1, name2 : type = value @[code] @[section]Data Types@[section] Dao supports a rich set of types to allow writing expressive codes. @[subsection]Basic Types@[subsection] The basic types are the following: @[table] ##Type Name ##Type Description ##none &&none value type ##bool &&boolean type ##int &&64 bit integer type ##float &&64 bit floating point number type ##complex &&complex number type ##string &&string type ##enum &&enum symbol type @[table] Though @[code]enum@[code] is a basic type, its type names may vary and include symbol names that are associated with the enum types. Examples, @[code] var name: string var state: enum = $on @[code] @[subsection]Aggregated Types@[subsection] The essential aggregated types in Dao are the followings: @[table] ##Type Name ##Type Description ##array < ElementType > &&array type ##tuple < ItemTypesOrNamesTypes > &&tuple type ##list < ItemType > &&list type ##map < KeyType, ValueType > &&map type @[table] These are template-like types that can take type arguments. The type arguments usually indicate what types of values the aggregated types may contain. For example, @[code] array # Array that may contain integers; list # List that may contain strings; map # Map that may contain pairs of string and int; tuple # Tuple that may contain one string and one integer; # The following is a tuple type with named items: # The first item is named "name" and has type "string"; # The second item is named "index" and has type "int"; tuple @[code] Examples of type annotations with these types, @[code] var vector: array = [ 1, 2, 3 ] var tuple1: tuple = ( "abc", 123 ) var tuple2: tuple = ( 'abc', 123 ) var names: list = { "Joe" } var table: map = { "abc" => 123 } @[code] @[subsection]Type Holder Types@[subsection] Type holder types are type names with a @[green]@@[green] prefix. They represent to-be-defined types, the same type holder represents the same type within a local context (a single composite type or a function prototype etc). They are usually used in template-like types as place holders for type arguments, and in the routine signatures of the methods of those types. This way, both the types and their methods are made generic, and can be instantiated (specialized) for particular argument types. For example, the list template type is declared as @[code]list<@T=any>@[code], where @[code]any@[code] is the default argument type for the list type. This means @[code]list<>@[code] will be equivalent to @[code]list@[code]. One of the list methods is the following, @[code] append( self: list<@T>, item: @T, ... : @T ) => list<@T> @[code] Within this routine signature, all @[green]@T@[green] represent the same type. So this method means, for a list of type @[green]list<@T>@[green] (@[green]@T@[green] is @[green]string@[green] for example), only items of type @[green]@T@[green] (@[green]string@[green]) can be appended to the list. Here is a simpler example, @[code] tuple<@T,@T> @[code] represents a tuple type whose two items must have the same type. @[subsection]Any Type@[subsection] Special type @[code]any@[code] cab be used to represent any type. Declaring a variable with @[code]any@[code] type will effectively disable compiling time type checking on the variable! @[subsection]Value Type@[subsection] Another special type in Dao is the value type, which, in addition to define type traits and information like the ordinary types, also defines a constant value. When a value or another value type is checked against a value type, not only the types should match, but also the values as well. The @[code]none@[code] is supported as a value type, it is designed this mainly because, @[code]none@[code] only has one unique value. The @[code]none@[code] can be used any where like an ordinary type, but it is mostly used in variant types to indicate a variable or parameter may also hold a @[code]none@[code] value (see the next subsection). Other value types cannot be used every where like ordinary types, they mainly appear as type arguments in composite types. For example, @[code] tuple<1,2> @[code] represents a tuple type whose first item must be @[code]1@[code] and its second must be @[code]2@[code]! Such value types are mainly useful when dealing with C++ template types. @[subsection]Variant Type@[subsection] 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] var intstr : int|string = 123 intstr = 'abc' @[code] In Dao, only variables of @[code]none@[code] type or unspecific types such as @[code]any@[code] and undefined type holder types can hold @[code]none@[code] value. But some times, some variables may need to or be known to hold either a @[code]none@[code] value or a value of a specific type, to allow this in the type checking, one needs use @[code]some_type|none @[code] for these variables. For example, @[code] routine FindWord( source: string, word: string ) => int|none { var pos = source.find( word ) if( pos < 0 ) return none return pos } var pos1 = FindWord( "dao language", "dao" ) # pos1 = 0 var pos2 = FindWord( "dao language", "lua" ) # pos2 = none @[code] Please see @[node]dao.type.variant@[node] for more information. @[subsection]Routine (Function) Types@[subsection] Routine (function) types are composed of keyword @[code]routine@[code] with other types, they are usually automatically generated for routines from their declarations. For example, @[code] routine Test( name: string, value = 123 ) { return value * 10; } io.writeln( std.about(Test) ) # Output: Test{routineint>}[0x7f9ef1c50340] @[code] In the output @[code]routineint>@[code] is the automatically created type for the routine @[code]Test@[code]. Here, @[list] --@[code]name:string@[code] : type information for the first parameter; it means the first parameter is named @[code]name@[code] and has type @[code]string@[code]. --@[code]value=int@[code] : type information for the second parameter; it means that the second parameter is named @[code]value@[code], and it has type @[code]int@[code] and a defualt parameter value; --@[code]=>int@[code] : (inferred) type information for the returned value; @[list] For code section routines, their type names have additional parts. For example, one of the list sorting method is a code section method with the following signature (prototype), @[code] sort( self: list<@T>, part = 0 )[X: @T, Y: @T => int] => list<@T> @[code] Its type name is, @[code] routine,part=int=>list<@T>>[X:@T,Y:@T=>int] @[code] Here @[code][X:@T,Y:@T=>int]@[code] is the additional part with respect to types for normal routines, and means that the code section may take two parameters of type @[code]@T@[code] and is expected produce an integer from evaluating the code section. Of course, the routine types can also be declared explicitly. So one can annotation a variable with the following routine type name: @[code] var myfunc: routineint> @[code] @[subsection]Generic Types@[subsection] The standard aggregated types such as list and maps etc. in Dao are supported as builtin generic types. Such support is also extended to user defined types, but such generic types can only be defined in C modules. The use of such types is exactly the same way as the standard generic types. One typical example for such generic types is the future value type @[code]mt::future<@V>@[code]. Though @[code]future@[code] is a builtin type, user can define customized types in exactly the same way as defining @[code]future@[code]. Please see @[node]daovm.interface.extending@[node] for the techniques of defining such types. @[comment] 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). @[comment] @[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 type Point3D = tuple @[code] @[text] @[name] dao.type.variant @[name] @[title] Variant (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 @[code]|@[code] as the delimiter. A variable of a variant type can hold value of any of the alternative types. For example, @[code] var intstr : int|string = 123 intstr = 'abc' @[code] Please note that a variant type normally 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. The simplest way to check the value type of a variant type variable is to use the type-swith construct: @[code] switch( intstr ) type { case int : io.writeln( intstr * 1000 ) case string : io.writeln( intstr + 'abcdefg' ) } @[code] Here in each type case, the variable can be used as the corresponding type without explicit casting. However, if a variant is compose of only two types and one of them is a @[code]none@[code] type, the variant can be used directly as the other type without explicity casting: @[code] routine Test( maybelist: list|none ) { if( maybelist == none ) return maybelist.append( 123 ) # No explicit casting; } @[code] The above mentioned type-switch construct has a couple of variants: @[code] switch( var name = expression ) type { ... } switch( invar name = expression ) type { ... } @[code] 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 block. More examples: @[code] var intstring: list = {}; 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|string ) { switch( data ) type { case int : io.writeln( 'handling int' ); case float : io.writeln( 'handling float' ); case string : io.writeln( 'handling string' ); } } Test2( 1 ); Test2( 1.0); 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, tuple, ... # one can use disjoint union to simplify this. operator[]( i: int, js: tuple )=>array { # one can simply return image[i,js], but the following is for demonstration purpose: var j1 = 0; var j2 = image.dim(1) - 1; if( js[0] != none ) j1 = js[0]; if( js[1] != none ) j2 = js[1]; return image[i,j1:j2]; } } var image = FakeImage(); io.writeln( image[1,1:] ); io.writeln( image[2,:1] ); @[code] @[code] routine Sum( alist : list<@T> ) => @T { # reflect.trace(); return alist.sum(); } var s = Sum( { 1, 2, 3 } ); #s += 'a'; # typing error io.writeln( s ); var s2 = Sum( { 'a', 'b', 'c' } ); io.writeln( s2 ); @[code] @[text]