load help; @[name] dao.tutorial @[name] @[title] Quick Guide @[title] @[text] This quick guide will cover some basics of Dao programming. We will start with the traditional @[green]hello world@[green] example. @[subsection] Hello World! @[subsection] To simply write "Hello world!" to the screen, one can use, @[code] io.write( 'Hello world!' ) @[code] In this simple example, the built-in @[green]io@[green] module is used to access the IO functionalities of the module. @[green]write()@[green] is a method provided by the @[green]io@[green] module to write outputs on to the standard output (the screen in this case). In Dao, string can be quoted with a pair of single quotation marks or with a pair of double quotation marks. Semicolons are optional at the end of statements. @[subsection] Running Scripts @[subsection] There are three ways to run codes in Dao. The most typical way is to create a script file (e.g. @[cyan]hello_world.dao@[cyan]), and put the codes into this file, and then run @[green]dao@[green] using this file as argument, @[code] $$ dao hello_world.dao @[code] But if you just want to run the codes once, you can simple run them from command line using, @[code] $$ dao -e "io.write( 'Hello world!' )" @[code] For learning Dao, it may be the best to run codes in interactive mode. Invoking @[green]dao@[green] without any argument will start an interactive shell, @[code] $$ dao Dao Virtual Machine 2.0 Built date: Jun 12 2014 Changeset ID: FOS.4d5eb15f0e53 Copyright(C) 2006-2014, Fu Limin Dao is released under the terms of the Simplified BSD License Dao Language website: http://daoscript.org (dao) @[code] @[subsection] Accessing Online Help @[subsection] If you have the Dao @[green]help@[green] properly installed, starting @[green]dao@[green] in interactive mode will automatically load the help files and print out the following information, @[code] Module Help is loaded. Now you may run "help()" to list all the available help entries; or run "help('help')" for detailed information about the help system. @[code] If you are reading this tutorial from a webpage, and want to access it from the interactive command line, you can simple run, @[code] (dao) help( 'dao.tutorial.basics' ) @[code] Such online helps come in two languages: English and Chinese. Currently the English version is more complete. To choose the language, @[code] help::set_language("en"); # choose english; help::set_language("zh"); # choose chinese; @[code] @[subsection] Commenting Codes @[subsection] It is always a good idea to comment your codes. Dao supports both single line comments and multiple line comments. A single comment starts at a @[green]#@[green] (not followed by a left curly bracket @[green]{@[green]), and ends at the end of the current line. And a multiple line comment (or just part of a single line) opens by @[green]#{@[green] and closes by @[green]#}@[green]. For example, @[code] # This is a simple demo: io.write( "Hello World!" #{ comment inside codes #} ); #{ Here are multi-lines comments. Here are multi-lines comments. #} @[code] @[subsection] Constants, Variables and Invariables @[subsection] Dao supports the explicit declaration of constants, local variables, static variables, global variables and invariables. Constants are declared with keyword @[green]const@[green], @[code] const DEFAULT_INDEX = 123 const DEFAULT_NAME = 'abc' @[code] Constants can only be initialized with constant expression. Constants that are declared at the top lexical scope are global constants. Keyword @[green]var@[green] can be used to declare local and global variables. If used at the top lexical scope, it will declare global variables, otherwise the declared variables will be local. @[code] var current_index = 456 # global variable; var current_name = 'def' # global variable; if( current_index ) { var temp_index = current_index # local variable; } @[code] In every places where @[green]var@[green] can be used, @[green]invar@[green] can be used as well, to declare local, global or member invariables. Invariables are special kind of variables that cannot be modified once initialized. @[code] var varlist = { 123 } invar invlist = varlist invlist.append( 456 ) # Error! @[code] Please see @[node]dao.data.invar@[node] for more information. Another type of variable is the static variable which can be declared with the @[green]static@[green] keyword. Outside class body, a static variable is a global variable with strictly local visibility within its declaration scope. So if a routine with static variables is executed multiple times, all the executions will access the same static variables. Static variables must be initialized with constant expressions. Without the above specifiers, newly declared symbols in the form of @[code]name=expression@[code] will be automatically declared as local variables. But if the symbol was defined as variable, such statements will simply reinitialize the existing variable. To avoid this, one must the keyword @[green]var@[green] to explicitly declare the variable as local, @[code] var index = 789 for( i = 1 : 3 ){ var index = 123 # Local to the loop; } @[code] Please read @[node]dao.data@[node] for more information. @[red] Note: in interactve mode, all top level variables are automatically declared as global variables. @[red] @[subsection] Type Annotation @[subsection] In the above examples, no type information is declared for the constants, variables and invariables. Their types are automatically inferred from the values that were used to initialize them. In many cases, it is better to provide type annotations to the constant, variable and invariable declarations. In general, programs with good type annotations are more readable them programs without them. Type annotations are usually placed after the constant/variable/invariable names with a colon sparating them. @[code] const vector: array = [ 12.5, 34.6 ] var varlist: list invar invlist: list = { 12.5, 34.6 } @[code] Here without type annotation, "vector" will become a constant of type "array". And "varlist" is declared without initializing expression, so its type cannot be inferred here. As you will see, type annotations can also be used in routine/function signatures and class member fields. Please read @[node]dao.type@[node] for more information. @[subsection] Control Structures @[subsection] @[subsection] Routines (Functions) @[subsection] @[subsection] Classes @[subsection] @[subsection] Error Handling @[subsection] @[text] @[name] dao.tutorial @[name] @[title] Tutorial @[title] ################################################################################ ################################################################################ ######## Types ################################################################################ ################################################################################ @[name] dao.tutorial.types @[name] @[title] Essential Types @[title] @[text] Dao supports a number of essential types as built-in types to allow flexible use of them. These types include several number types, enum symbol, string, numeric array, tuple, list and map etc. @[subsection] Numbers @[subsection] Dao has native supports for the following number types: @[green]int@[green], @[green]float@[green], @[green]double@[green], @[green]complex@[green] and @[green]long@[green]. Integer and floating point numbers can be expressed in the same ways as in most other languages. Floating point numbers in Dao are parsed as double precision floating point numbers @[green]double@[green] by default. To explicitly express a single (double) precision floating point number @[green]float@[green] (@[green]double@[green]), it is necessary to append a suffix @[green]F@[green] (@[green]D@[green]) to a normally expressed number. The imaginary part of a @[green]complex@[green] number can be expressed as an @[green]int@[green] or @[green]float@[green] number with a @[green]C@[green] suffix. A general @[green]complex@[green] number can be expressed by combining an integer or a floating point number as the real part and an imginary part, which are both stored as double precision floating point numbers. @[green]long@[green] type represents arbitrary precision integers. They can be expressed as an @[green]int@[green] number with a @[green]L@[green] or a @[green]Lx@[green] suffix, where @[green]x@[green] is an integer between 2 and 16 (inclusive), representing the base of the number (used for parsing and printing only). Examples, @[code] I1 = 123 I2 = 0xabc F1 = 456.7F F2 = 123e4F D1 = 123D D2 = 456.7 D2 = 456.7D D3 = 123E4 D3 = 123E4D C1 = 123C C2 = 456.6C L1 = 123456789L L2 = 10001001110L2 L3 = 0x123abc456L16 @[code] These types support most of the common operators that are meaningful for them. For example, all the basic arithmetic operators such as: @[green]+@[green] (addition, unary plus), @[green]-@[green] (subtraction, unary minus), @[green]*@[green] (multiplication), @[green]/@[green] (division), @[green]%@[green] (modulo) and @[green]**@[green] (power) are supported. @[code] I1 = 123 + 456 I2 = 789 % 123 F1 = 123.5 ** 3 D1 = 789.5D / 123 L1 = 123456789L ** 123L @[code] Please see @[node]dao.type.int@[node], @[node]dao.type.float@[node], @[node]dao.type.double@[node], @[node]dao.type.complex@[node] and @[node]dao.type.long@[node] for more information. @[subsection] Enum Symbols @[subsection] @[green]enum@[green] is a type hybriding C++ enum and Ruby symbol types to offer the advantages of both types. It can be used as enumerated constant in C++ or as symbol in Ruby. Enum types can be declared in the same way as C++ enum: @[code] enum MyEnum { AA, BB, CC } @[code] Then it can be used in the following way: @[code] e1 = MyEnum::BB # with constant folding; e2 = MyEnum.BB # without constant folding; e3 : MyEnum = $BB # with auto conversion; @[code] All @[cyan]e1@[cyan], @[cyan]e2@[cyan] and @[cyan]e3@[cyan] will be an enum value with integer value equal to 1 and string symbol equal to "BB". The last is equivalent to: @[code] # Use enum type on the fly: e3 : enum = $BB @[code] Enum values can be used in switch-case statements, which can be optimized into table lookup, if the enums are explicitly typed or can be inferred. For example, @[code] switch( (any)e1 ){ case MyEnum::AA : ... case MyEnum::BB : ... case MyEnum::CC : ... } switch( e1 ){ case $AA : ... case $BB : ... case $CC : ... } @[code] Both will be optimized into table lookup, because the case values have well defined types (the cases in the second example have types inferred from the expression inside @[cyan]switch()@[cyan] ). @[subsection] Strings @[subsection] In Dao, a string can be expressed as a sequence of characters enclosed by a pair of single qutotation marks or a pair of double quotation marks. The characters placed between the quotation marks must be formated according some rules (such as escaping quotation marks and other special characters and using numeric encodings of characters etc.). Please see @[node]dao.type.string@[node] for more information. @[code] mbs = 'str' wcs = "道语言" mbs2 = 'It\'s green' wcs2 = "\u9053\u8bed\u8a00" # the same as wcs: "道语言"; @[code] To use strings as it is written without special treatment of the characters, they can be expressed as @[green]verbatim@[green] string, which are quoted with a pair of identical compound marks in the forms of @[green]@[]@[green] and @[green]@@[]@[green]. Any number of letters, digits, underscores, blank spaces, dots, colons, dashes and assignment marks can be placed in between the squared brackets to make sure the marks will not appear inside the string. The difference between using @[green]@[]@[green] and @[green]@@[]@[green] is the same as the difference between using single quotation marks and double quotation marks. Please see @[node]dao.type.string@[node] for more information. @[code] # C++ codes in MBS: cpp = @[cpp x] class AA { int index; }; struct BB{}; @[cpp x] # Lua codes in WCS: lua = @@[lua] local a = 1; function Test() io.write( 'Hello' ) end @@[lua] @[code] The content of a string can be accessed or altered using sub-indexing or slicing: @[code] str = 'ABCDEFGHIJK'; io.writeln( str[1] ) # the second character; io.writeln( str[:4] ) # the substring from the start to the 4th character; io.writeln( str[6:] ) # the substring from the 6th character to the end; io.writeln( str[3:8] ) # the substring from the 3rd to the 8th character; # Set single character: str[1] = 'X'; str[1] = 'X'[0]; # Set a substring: str[2:5] = '1234' # str = 'AB1234GHIJK' str[2:5] = '123456' # str = 'AB123456GHIJK' str[2:5] = '12' # str = 'AB12GHIJK' # Using negative index: io.writeln( str[-1] ) # the last character; io.writeln( str[-2] ) # the second last character; io.writeln( str[-2:] ) # the last two characters; @[code] String can be concaternated using @[green]+@[green] or @[green]+=@[green], @[code] str = 'ABCDEFGHIJK'; str2 = str + '123' # str2 = ABCDEFGHIJK123 # Append a string: str += '123' # str = ABCDEFGHIJK123 # Append a character: str += 88 # str = ABCDEFGHIJK123X @[code] @[subsection] Arrays @[subsection] Dao has built-in support for multi-dimensional numeric arrays. Such arrays can be defined by using the squared brackets @[green][]@[green] or @[green]array{}@[green]. Using such constructs, one can either enumerate all the elements as a vector/matrix, or specify an arithmetic progression with a start value, a step value and the number of steps. If the step value is omitted, it will be assumed to be zero. @[code] vec1 = [1, 2, 3] # array vector, or 1x3 matrix; vec2 = [1.0; 2; 3] # array 3x1 matrix, or transposed vector; mat1 = [1D, 2; 3, 4] # array 2x2 matrix; mat2 = [ [1, 2], [3, 4] ] # 2x2 matrix mat3 = [ [1, 2, 3] : 5 ] # 5x3 matrix; mat4 = array{ 1, 2; 3, 4 } # 2x2 matrix @[code] Like string, array support sub-indexing, slicing and negative indices: @[code] mat = [ 1, 2, 3; 4, 5, 6; 7, 8, 9 ]; # 3x3 matrix; rowvec = mat[1,:] # the second row; colvec = mat[:,1] # the second column; submat1 = mat[:1,:] # the first two rows; submat2 = mat[:,1:] # the last two columns; submat3 = mat[:1,1:] # intersection between the first two rows and the last two columns; mat[0,:] = [11, 22, 33] # set the first row to [11, 22, 33]; mat[:,1] += [11, 22, 33] # add [11, 22, 33] to the second column; mat[:,1] += 100 # add 100 to the second column; mat[:1,1:] += [10, 20; 30, 40] # add [10, 20; 30, 40] to sub-matrix of mat; @[code] Please see @[node]dao.type.array@[node] for more information. @[subsection] Lists @[subsection] List can be created in similar ways as array, by enumerating elements or specifying an arithmetic progression, but using @[green]{}@[green] or @[green]list{}@[green] instead of @[green][]@[green] or @[green]array{}@[green]. @[code] list1 = { 1, 2, 3 } # list list2 = { 1.0, 2, 3 } # list list3 = { 1 : 2 : 5 } # list list4 = { 'abc', 'def' } # list list5 = { 123, 'abc' } # list list6 = list{ 'a' : 3 } # { 'a', 'a', 'a' } list7 = { 'a' : 'b' : 3 } # { 'a', 'ab', 'abb' } @[code] List also supports sub-indexing, slicing and negative indices: @[code] alist = { 0, 1, 2, 3, 4, 5 } item = alist[1] item = alist[-2] sublist = alist[2:4] alist[3] = 10 alist[4] += 10 @[code] @[subsection] Maps and Hash Maps @[subsection] A map or hash map organizes a set of key/value pairs into a structure for efficient lookup. The keys in a map are ordered, while the keys in a hash map are unordered. A map can be created using @[green]{key=>value...}@[green] or @[green]map{key=>value...}@[green]. Replacing the @[green]=>@[green] with colon @[green]->@[green] will create hash maps. Map and hash map can be used in identical ways. @[code] amap = { 'abc' => 123, 'def' => 456 } ahash = { 'abc' -> 123, 'def' -> 456 } amap = map{ 'abc' => 123, 'def' => 456 } ahash = map{ 'abc' -> 123, 'def' -> 456 } @[code] Sub-scripting and slicing are also supported for map to access value(s) through key(s). @[code] amap = { 'abc' => 123, 'def' => 456, 'ghi' => 789 } value = amap[ 'abc' ]; submap = amap[ 'abc' : 'def' ]; @[code] @[subsection] Tuples @[subsection] Tuple is a very handy type, which can be used to hold a fixed number of items, with type information recorded for each of them. In a tuple, each item can have a name, which can be used to access the item as field. They can be created in similar ways as creating lists and maps, but use @[green]()@[green] instead. @[code] tup1 = ( 123, 'abc' ) # tuple with unnamed items; tup2 = ( index => 123, 'abc' ) # the first item is named as "index"; tup3 = tuple{ 123, name => 'abc' } # the second item is named as "name"; @[code] Each item of a tuple can be accessed using its index or field (item name). New tuples can be created from other tuples by slicing. @[code] tup = ( index => 123, 'abc', [1,2,3] ) id = tup[0] id = tup.index tup.index = 456 tup2 = tup[:1] # ( index => 123, 'abc' ) @[code] @[text] ################################################################################ ################################################################################ ######## Controls ################################################################################ ################################################################################ @[name] dao.tutorial.controls @[name] @[title] Constrol Structures @[title] @[text] Control structures are essential for a program to do complex work. Dao supports the common controls such as: @[green]if-else@[green], @[green]for@[green], @[green]while@[green], @[green]do-while@[green], @[green]switch-case@[green], @[green]break@[green] and @[green]skip@[green] etc. @[subsection] If-Else @[subsection] The @[green]if-else@[green] control allows the program to branch and execute different blocks of codes, based on the results of the condition expressions. If a condition is true, the code block that is nested under the @[green]if@[green] or @[green]else if@[green] statement will be executed: @[code] if( expr1 ){ block1; }else if( expr2 ){ block2; }else{ block3; } @[code] If @[cyan]expr1@[cyan] is true, @[cyan]block1@[cyan] is executed; otherwise, if @[cyan]expr2@[cyan] is true, @[cyan]block2@[cyan] is executed; otherwise, @[cyan]block3@[cyan] is executed; zero or more @[green]else if@[green] and zero or one @[green]else@[green] statement can be used. @[code] if( 2 > 1 ) io.writeln("2 is larger than 1."); @[code] Before each condition expression, there can be an optional expression or variable declaration, for example, @[code] if( rnd = rand(100); rnd > 50 ) io.writeln( "random number is >50" ); @[code] @[subsection] For @[subsection] Dao supports different styles of for-looping, the simplest one is probably the following, @[code] for( variable = init_value : step_value : max_value ){ block; } @[code] For this loop, the @[cyan]init_value@[cyan] will be first assigned to @[cyan]variable@[cyan], and then compared to the @[cyan]max_value@[cyan] to test if it is smaller than @[cyan]max_value@[cyan], if yes, the execution enters the loop. After each cycle of the loop, the @[cyan]step_value@[cyan] (or one if @[cyan]step_value@[cyan] is omitted) is added to @[cyan]variable@[cyan], then the comparison and testing is repeated to determine whether to enter the loop or exit the loop. C/C++ style @[green]for@[green] looping is supported by Dao: @[code] for( init; condition; step ){ block; } @[code] The execution sequence of @[green]for@[green] statement is the following: @[list] == execute initial expression @[cyan]init@[cyan], and goto 3; == execute @[cyan]step@[cyan]; == evaluate the condition expression @[cyan]condition@[cyan]; == check the value of @[cyan]condition@[cyan]: if true, goto 5; otherwise, goto 6; == execute @[cyan]block@[cyan], and goto 2; == stop looping; and start to execute the statements after the loop body. @[list] Dao also supports @[cyan]for-in@[cyan] loop, @[code] for( item in list ){ block; } @[code] Multiple @[cyan]in@[cyan] can appear in one loop, and the items of the same indices from multiple lists are taken in each cycle. The loop is terminated after all the items in the shortest list have been iterated. @[code] for( item1 in list1; item2 in list2; ... ){ block; } @[code] @[cyan]for-in@[cyan] can also be used for maps, @[code] for( item in a_map ){ block; } @[code] Examples, @[code(dao)] for( i=0; i<3; ++i ){ io.writeln( i ); } hash = { "b" => 11, "a" => 22, "e" => 33, "c" => 44 }; for( a in hash.keys(); b in hash.values(); c in {1 : 1 : hash.size()-1 } ){ #if a == "a" break io.writeln( a, b, c ); } @[code(dao)] Note: if a single string is used in the condition expression in @[green]if,while,for@[green] statements, it returns true, if the string has length larger than zero, otherwise, returns false. @[subsection] While @[subsection] When a condition is true, @[green]repeatedly@[green] execute a block of codes: @[code] while( expr ){ block; } @[code] If @[cyan]expr@[cyan] is true, @[cyan]block@[cyan] is executed and repeated until @[cyan]expr@[cyan] becomes false, namely, while @[cyan]expr@[cyan] is true, execute @[cyan]block@[cyan]. @[code] i = 0; while( i < 5 ){ io.writeln( i ); i += 1; } @[code] Before the condition expression, there can be an optional expression or variable declaration, for example, @[code] while( rnd = rand(100); rnd > 50 ) io.writeln( "random number is >50" ); @[code] @[subsection] Do-While @[subsection] @[code] do{ block; } while ( condition ) @[code] Execute @[cyan]block@[cyan], and then repeat executing it when the @[cyan]condition@[cyan] is true. @[subsection] Switch-Case @[subsection] Switch-case control provides a convenient way to branch the code and choose a block of code to execute based on the value of a object. @[code] switch( value ){ case C_1 : block_1 case C_2 : block_2 case C_3 : block_3 ... default: block0 } @[code] If the @[cyan]value@[cyan] equals to @[cyan]C_i@[cyan], @[cyan]block_i@[cyan] will be executed. Here @[cyan]C_i@[cyan] must be a constant, but they can be of different types, that means, you can mix numbers and strings as case values. Unlike in C/C++, no @[green]break@[green] statement is required to get out of the @[green]switch@[green]. If you want to execute the same block of codes for different case values, you just need to organize them together in the following way: @[code] switch( value ){ case C1, C2, C3 : block3 ... default: block0 } @[code] Namely, Dao allows one case entry to have multiple values. In this way, @[cyan]block3@[cyan] will be executed for case values @[cyan]C1,C2@[cyan] and @[cyan]C3@[cyan]. As a simple example, @[code(dao)] a = "a"; switch( a ){ case 1, "a" : io.write("case 1 or a"); default : io.write("case default"); } @[code(dao)] Dao also allows the use of a value range represented as @[cyan]start ... end@[cyan] as case entry, so that the corresponding code block is executed if the value in switch falls inside the range. The ranges must not be overlapping. @[code(dao)] switch( 5 ){ case 1 ... 4 : io.writeln( 'case 1-4' ); case 5 ... 9 : io.writeln( 'case 5-9' ); case 10 ... 11 : a = 1; } @[code(dao)] @[subsection] Break and Skip @[subsection] @[green]break@[green] can be used to exit a loop, and @[green]skip@[green] can be used to skip the rest part of script and start the next cycle of a loop. @[green]skip@[green] is equivalent to @[green]continue@[green] in C/C++. @[code] for( i=0; i<5; ++i ){ io.writeln( i ); if( i == 3 ) break; } @[code] @[subsection] Deferred Block @[subsection] Deferred block is a block that will not be executed immediately when the normal execution reaches it, instead, its execution will be deferred until the exit of the function after the function is completed or has encounted an exception. Such blocks are marked with the @[green]defer@[green] keyword, and placed inside of a pair of curly brackets: @[code] defer { block } @[code] This is very useful for freeing resources allocated in functions that have multiple exits. For example, one can defer a block to close a file handle immediately after the file handle is opened: @[code] routine Test( id ) { fout = io.writeln( 'output.txt', 'w+' ) defer { fout.close() } fout.writeln( 'abc' ) if( id == 0 ){ fout.writeln( '123' ) return } io.writeln( 'def' ) } @[code] As shown in the above example, a deferred block can access outer scope constants and varaibles in the same way as closures. These outer scope variables are captured at the time the deferred block is reached in the normal execution. @[code] routine Test() { for( i = 1 : 3 ) defer { io.writeln( 'deferred', i ) } } Test() @[code] This will print: @[code] deferred 3 deferred 2 deferred 1 @[code] When a function exits, all the deferred blocks that have been reached in the normal execution will be executed in the reverse order of being reached. Any deferred block can modify the value returned by the function. In order to do this, one must designate a variable name for the returned value, by placing the name inside a pair brackets after the @[green]defer@[green] keyword: @[code] defer (ret) { ret += 1 } @[code] For example, @[code] routine Test() { defer ( result ) { result *= 2 # double the returning value } return 1000; } io.writeln( Test() ) @[code] This will print out 2000. @[text] ################################################################################ ################################################################################ ######## Routine ################################################################################ ################################################################################ @[name] dao.tutorial.routine @[name] @[title] Routine and Decorator @[title] @[text] Routine is a relative independent block of codes that can be reused by invoking it at places where it is needed. It can accept parameters to changes its behaviour. It may also return results to its caller. @[subsection] Definition @[subsection] Dao routines are declared with keyword @[green]routine@[green] (or @[green]function@[green] or @[green]sub@[green], which are exactly equivalent to @[green]routine@[green]). For example, @[code(dao)] routine func( a, b ) { a += 10; b += "test"; return a, b; # return more than one results. } ( r1, r2 ) = func( 111, "AAA" ); r3 = func( r1, r2 ); @[code(dao)] defines a function that can take two parameters as input, and return two values as output. @[subsection]Named Parameter@[subsection] In Dao the function parameters are named, and parameter values can be passed in by name: @[code] func( b => 123, a => "ABC" ); @[code] @[subsection]Parameter Type and Default Value@[subsection] It is also possible to specify the type and/or the default value of a parameter. @[code] routine MyRout( name : string, index = 0 ) { io.writeln( "NAME = ", name ) io.writeln( "INDEX = ", index ) } @[code] Here @[cyan]name@[cyan] is specified as string, and @[cyan]index@[cyan] is specified as an integer with default value 0. Any parameter after a parameter with default value must have default values as well. If a routine is called with wrong type of parameters, or no value is passed to a parameter without a default value, an exception will be raised and the execution will abort. @[subsection]Routine Overloading@[subsection] Routine overloading by parameter types is also supported in Dao, which means that multiple routines can be defined with the same name, but different parameter signatures. @[code] routine MyRout( index : int, name = "ABC" ) { io.writeln( "INDEX = ", index ) io.writeln( "NAME = ", name ) } MyRout( "DAO", 123 ) # invoke the first MyRout() MyRout( 456, "script" ) # invoke the second MyRout() @[code] @[subsection]Routine As First Class Object@[subsection] Dao also support first class functions / routines. They can be created as anonymous function or closure, in the following way: @[code] foo = routine( x, y : TYPE, z = DEFAULT ) { codes; } @[code] The syntax is nearly identical to the definition of a normal function, except the following differences: @[list] == There is no need for a function name; == The expressions for default parameters do not need to be constant expressions, they are evaluated at running time when the function/closure is created; == The function body may contain variables defined in the "upper" function that creates it; depending on the type of the "upper" variable, its copy (for simple types) or reference will be used by the created function. @[list] When such function accesses local variables from its outer/upper scope, it is created as a closure, otherwise as an anonymous function. Here is a simple example, @[code] a = "ABC"; rout = routine( x, y : string, z = a+a ){ a += "_abc"; io.writeln( "lambda ", a ) io.writeln( "lambda ", y ) io.writeln( "lambda ", z ) } rout( 1, "XXX" ); @[code] @[subsection] Coroutine and Generator @[subsection] See @[node]module.core.coroutine@[node]. @[subsection] Code Section Methods @[subsection] Code section/block method is an alternative to functional methods in other languages such as Python. Dao code section is syntactically very similar to the code block in Ruby. Unlike Ruby code blocks which are compiled as closure and passed as parameter (so it's essentially a syntax sugar), Dao code section is really a code section in its host function, no closure is created a runtime. When needed, the method locate the code section in the host function and run that section of codes. To define a code section method, it will be necessary to specify two set of parameters and return types: one for the normal routine, and the other for the code section. @[code] routine meth_name( meth_params ) [sect_params => sect_return] => meth_return { ... } @[code] The parameter list signature @[cyan]sect_params@[cyan] for the code section specifies what kind of parameters this method will pass to the code section; and the section return type @[cyan]sect_return@[cyan] indicates what type of value this method expects the code section to return. Code section method can be called in the following way: @[code] returned = meth_name( meth_params ) { code_block } @[code] If there is no method parameter, it can be simply written as: @[code] returned = meth_name { code_block } @[code] By default, the code section receives the parameters passed in by the method through implicitly defined variables named @[green]X@[green] and @[green]Y@[green]. User can choose to use more meaningful names by, @[code] returned = meth_name { [index, item] code_block } @[code] For example, list type has a code section method for sorting with the following signature, @[code] sort( self :list<@T>, k=0 ) [X :@T, Y :@T => int] => list<@T> @[code] Here the code section parameters @[cyan]X@[cyan] and @[cyan]Y@[cyan] are used to pass two items of the list for comparison. The code section return type @[green]int@[green] indicates that the code section is expected to return an integer as the comparison result. So this @[green]sort()@[green] can be use in the following ways, @[code] numlist = { 11, 44, 21, 32, 56, 67, 25 } # Sort all by ascend order: numlist.sort { X < Y } # Sort by descend order until the largest 3 items are sorted: numlist.sort( 3 ) { X > Y } # Now the first 3 items of the list is the largest 3 items; tuplist = { ( 2, 'ghi' ), ( 1, 'def' ), ( 2, 'abc' ), ( 1, 'abc' ) } tuplist.sort { # First sort by the first items of the tuples; if( X[0] != Y[0] ) return X[0] < Y[0]; # Then sort by the second items; return X[1] < Y[1]; } @[code] In a user defined code section method, the @[green]yield@[green] statement can be used to pass parameters and invoke the execution of the code section that is attached to the call. Here is an example for user defined code section method, @[code] # A function that can be called with a code section. # The code section is expected to take an integer as parameter, # and return a string. routine Test() [X :int => string] => string { io.writeln( 'In functional method!' ); s = yield( 123 ); # execute the code section; io.writeln( 'Yielded value:', s ); return s; } Test { io.writeln( 'In code section:', X ); return 'abc'; } @[code] @[subsection] Decorator @[subsection] @[red] Note: this feature is only functional in the online demos and the latest devel release. @[red] Decorators are a special type of functions that can be used to modify (decorate) other functions or create modified versions of these functions. There are two main ways to decorate a function: one is to specify one or more decorators for a function at its definition; the other is to call a decorator in an expression in the same way to call a function. Decorating a function at its definition will change that function, and decorating a function in an expression will create a modified copy of the function. A decorator can be defined in almost identical way as definition a normal function with a few exceptions. First, a decorator must be declared with a name prefixed with @[green]@@[green], and second, the first parameter must be a routine type, which determines which type of routines can be decorated by this decorator. A variable to hold the parameters that will be passed to the function, must also be declared inside a pair of brackets right after the name of the first parameter. For example, @[code] routine @Decorator( func(args) : routine, extra = 123 ) { io.writeln( 'Calling function:', std.about( func ), extra ); return func( args, ... ); # ... for parameter expanding; } @[code] This decorator can be applied to any function. In this decorator, the decorated function is called with @[green]args@[green], which is expanded for the call as indicated by @[green]...@[green]. @[green]args@[green] is a specially declared variable to hold the parameters that will be passed to the function. In the following example, the function is decorated and modified at its definition, @[code] # brackets can be omitted when the decorator take no parameter: @Decorator @Decorator( 456 ) routine Function() { io.writeln( 'Function()' ) } @[code] While in this one, a modified copy of the function will be returned, @[code] routine Function() { io.writeln( 'Function()' ) } func = @Decorator( Function ) @[code] If a decorator expression uses only constants, the decoration can be done at compiling time, @[code] const MyFunc = @Decorator( Function ) @[code] This feature can be exploited to create modified copies of existing functions that are imported or loaded from other modules, and use the same function names, @[code] load MyModule # MyFunction is defined in this module; # Create a modified copy and use the same name: const MyFunction = @Decorator( MyFunction ) @[code] Decorators can be overloaded just like normal functions, and be applied to overloaded functions, as well as class methods. Please see @[node]dao.routine.decorator@[node] for additional information. @[text] ################################################################################ ################################################################################ ######## Class ################################################################################ ################################################################################ @[name] dao.tutorial.class @[name] @[title] Class, Mixin, OOP and AOP @[title] @[text] Object-Oriented Programming (OOP) is supported in Dao by offering class-based features such as data abstraction, encapsulation, polymorphism and inheritance etc. And such support for OOP is further enhanced by interface. @[subsection]Class Definition@[subsection] A @[green]class@[green] is a user-defined data structure consisting data fields and member methods, which define the states and behaviours for the instances of the class. Class supports three types of fields: @[list] --@[green]constant@[green]: declared with keyword @[green]const@[green]; --@[green]static variable@[green]: declared with keyword @[green]static@[green]; --@[green]instance variable@[green]: declared with keyword @[green]var@[green]; --@[green]instance invariable@[green]: declared with keyword @[green]invar@[green]; @[list] Such fields can be declared with or without explicit types, and with or without default or initialization values, in the same way as specifying types and/or default values for function parameters. For example, the following can be used for instance variables, @[code] var variable; var variable = init_value; var variable : typename; var variable : typename = init_value; @[code] Class methods must be declared with keyword @[green]routine@[green] (or its alias keywords @[green]function@[green] or @[green]sub@[green]) for constructors and normal methods, or keyword @[green]operator@[green] for operator overloading. The access of class fields and methods can be restricted by three permission keywords: @[list] --@[green]public@[green]: publically accessible without restriction; --@[green]protected@[green]: accessible from the class and its derived classes; --@[green]private@[green]: only accessible from the class; @[list] Here is a simple class, @[code] class ClassOne { var index = 0; var name : string var words : list = {} routine ClassOne( name :string, index = 0 ){ self.name = name; self.index = index; } } @[code] Within class methods, the special variable @[green]self@[green] represents the current class instance. Class methods may be declared inside class body and defined outside in the same way as in C++, but in Dao, one should make sure that, the parameter list must be exactly the same in the places for declaration and definition. @[subsection]Class Instance@[subsection] Class constructors are the methods that have name the same as the class name. A class instance can be created by invoking a constructor of the class in the same way as a function call, @[code] object = ClassOne( 'abc' ) @[code] Like in Python, the constructors are not used to create class instances, instead, an instance is created before, and then the constructor is called after to initialize the instance. For a class without parent classes and without constructors, its instances may also be created by enumerating the members of the class, @[code] class Point3D { var x = 0D; var y = 0D; var z = 0D; } point = Point3D.{ 1, 2, 3 }; @[code] The names of instance variables may also be specified in enumeration, @[code] point = Point3D.{ y = 2, x = 1, z = 3, }; @[code] When you create a class instance using enumeration, the instance is created, and filled with the values in the enumeration. Instance creation by enumeration is much faster than creation by invoking class constructor, since no class constructor is called and there is no overhead associated with function call (parameter passing, running time context preparation for the call etc.). So such instance creation is very desirable for creating many instances for simple classes, in which there are no complicated initialization operations. @[subsection]Member Variable@[subsection] As mentioned above, instance variables are declared in class constructor using @[green]var@[green] keyword. Class constant can be declared using @[green]const@[green] keyword, and static member can be declared using @[green]static@[green] keyword as in C++: @[code] class Klass { const aClassConst = "KlassConst"; static aClassStatic; } @[code] Here @[cyan]aClassConst@[cyan] will be constant belonging to a @[cyan]Klass@[cyan]. While @[cyan]aClassStatic@[cyan] will be a static variable in the class scope. @[subsection]Setters, Getters and Overloadable Operators@[subsection] Instead of defining @[cyan]setXyz()@[cyan] methods, one can define @[cyan].Xyz=()@[cyan] method as setter operator, so that modifying class member @[cyan]Xyz@[cyan] by @[cyan]obj.Xyz=abc@[cyan] will be allowed; similarly, if @[cyan].Xyz()@[cyan] is defined, get the value by @[cyan]obj.Xyz@[cyan] will also be allowed: @[code(dao)] class MyNumber { private var value = 0; public routine MyNumber( v = 0 ){ value = v; } operator .value=( v ){ value = v; io.writeln( "value is set" ) } operator .value(){ return value } } num = MyNumber( 123 ) num.value = 456 io.writeln( num.value ) @[code(dao)] @[cyan] As you may guess, accessing instance variable through getters and setters are more expensive than using them as public variables! They should be used only when they make things more convenient (for example, when you want them to do extra work when a variable is accessed). @[cyan] Other supported operators for overloaing include: @[list] ==[operator ()(...)] for function call; ==[operator [](...)] for getting item(s); ==[operator []=(...)] for setting item(s); @[list] Basic arithmetic operators are also supported for overloading. @[subsection]Method Overloading@[subsection] Class methods can be overloaded in the same way as normal functions. Class constructor may also be overloaded by simply adding a method with the same name as the class. For example, class @[cyan]MyNumber@[cyan] can be modified to hold numeric value only: @[code(dao)] class MyNumber { private var value : int = 0; public routine MyNumber( value = 0 ){ # accept integer as parameter self.value = value; } # overloaded constructor to accept MyNumber as parameter: routine MyNumber( value : MyNumber ){ self.value = value.value } operator .value=( v : int ){ value = v } operator .value=( v : MyNumber ){ value = v.value } operator .value(){ return value } } num1 = MyNumber( 123 ) num1.value = 456 io.writeln( num1.value ) num2 = MyNumber( num1 ) io.writeln( num2.value ) num2.value = 789 io.writeln( num2.value ) num2.value = num1 io.writeln( num2.value ) @[code(dao)] @[subsection]Inheritance@[subsection] @[code] class ColorRBG { var Red = 0.0; var Green = 0.0; var Blue = 0.0; routine ColorRBG( r = 0.0, g = 0.0, b = 0.0 ){ Red = r; Green = g; Blue = b; } routine ColorRGB( name : enum ){ switch( name ){ case $white: Red = Green = Blue = 1.0 case $black: case $red: Red = 1.0 case $green: Green = 1.0 case $blue: Blue = 1.0 case $yellow: Red = Green = 1.0 case $magenta: Red = Blue = 1.0 case $cyan: Green = Blue = 1.0 } } routine setRed( r ){ Red = r; } routine setGreen( g ){ Green = g; } routine setBlue( b ){ Blue = b; } routine getRed(){ return Red; } routine getGreen(){ return Green; } routine getBlue(){ return Blue; } } yellow = ColorRBG( 1, 1, 0 ); # create an instance. @[code] The following will define a derived class of @[cyan]ColorRBG@[cyan], @[code] class ColorRGBA : ColorRBG { var alpha = 0.0; # alpha component for tranparency. routine ColorRGBA( r = 0.0, g = 0.0, b = 0.0, a = 0.0 ) : ColorRBG( r, g, b ){ alpha = a; } # Inherit the constructor from ColorRGB that accepts color names: use routine ColorRGB( name : enum ); } yellow2 = ColorRGBA( 1, 1, 0, 0 ); # not tranparent. yellow2.alpha = 0.5; # change to half tranparency. magenta = ColorRGBA( $magenta ) @[code] In the definition of derived class, the parent class @[cyan]ColorRBG@[cyan] should be put after the derived class and be separated with @[green]:@[green]. (Since 2013-10-20, classic multiple inheritance is no longer support. Now mixins are the preferred way to do similar things.) The constructor parameters for derived class can be passed to parent classes in the way as shown in the example. Constructors from the parent class can be inherited by using the @[green]use@[green] statement, in which the full function signature of the constructor should be specified. @[subsection] Mixin @[subsection] @[red] Features introduced in the this and the remaining sections are available only in the latest devel release and the online version. @[red] Classes to be used as mixins can be specified in a pair of brackets following the class name. Only classes without parent classes can be used as mixins. @[code] class Base { var value = 456 routine Meth2(){ io.writeln( self, value ) } } class Mixin ( Base ) { var index = 123 routine Meth(){ io.writeln( self, index, value ) } routine Meth2( a : string ){ io.writeln( self, index, value, a ) } } # # The "Base" class will be presented only once in "Klass": # class Klass ( Base, Mixin ) { var index = 123456 routine Meth2( a : int ){ io.writeln( self, index, value, a ) } } k = Klass() io.writeln( k.index ) k.Meth() k.Meth2() k.Meth2( 'abc' ) k.Meth2( 789 ) @[code] @[subsection] Class Decorator @[subsection] (New in the latest devel release and online version) Class decorators are classes that can be used modify other classes. The modification is done by using such class as a mixin base class to inject (combine) its members into the modified class, and by automatically applying its method decorators to the methods of the modified class. For such auto decorator application, only decorators with explicitly specified decoration targets are automatically applied. Such targets are expressed as prefix and suffix rules which can be expressed in the following ways: @[list] == Prefix~ : a prefix pattern. The decorator will be auto applied to methods that have names with such prefix; == ~Suffix : a suffix pattern. The decorator will be auto applied to methods that have names with such suffix; == Prefix~Suffix : a prefix and suffix pattern. The decorator will be auto applied to methods that have names with such prefix and suffix; == ~ : an empty prefix and suffix pattern. The decorator will be auto applied to any methods. @[list] When multiple mixins are used in a host class, the decorators of the first mixin are applied the last. And the first decorator of the same decorator is also applied the last as well. @[code] class @Header { static routine @Delimiter( meth(args) : routine ) for ~ { io.writeln( '=======================' ) return meth( args, ... ) } routine @Delimiter( meth(args) : routine ) for ~ { io.writeln( '-----------------------' ) return meth( args, ... ) } } class @Decorator { var value = 654321 routine @Test( meth(args) :routine ) for Test { io.writeln( 'Decorator::Test()', value ) meth( args, ... ); } routine @Prefix( meth(args) :routine ) for Prefix~ { io.writeln( 'Decorator::Prefix()' ) meth( args, ... ); } routine @Suffix( meth(args) :routine ) for ~Suffix { io.writeln( 'Decorator::Suffix()' ) meth( args, ... ); } routine @Prefix_Suffix( meth(args) :routine ) for Prefix~Suffix { io.writeln( 'Decorator::Prefix_Suffix()' ) meth( args, ... ); } } class MyMixin ( @Header, @Decorator ) { routine Test(){ io.writeln( 'MyMixin::Test()' ) } routine PrefixTest(){ io.writeln( 'MyMixin::PrefixTest()' ) } routine TestSuffix(){ io.writeln( 'MyMixin::TestSuffix()' ) } routine PrefixTestSuffix(){ io.writeln( 'MyMixin::PrefixTestSuffix()' ) } } obj = MyMixin() obj.Test() obj.PrefixTest() obj.TestSuffix() obj.PrefixTestSuffix() @[code] @[subsection] Aspect Class @[subsection] (New in the latest devel release and online version) In Dao, a class decorator can be effectly used as an aspect for AOP, if decoration target patterns are specified for auto application. The target patterns are can be specified in the same way as the target patterns for class method decorators. The fields of such class will be automatically injected to normal classes selected according to the affix rules, and the decorators defined in such aspect class are automatically applied to the methods (selected according to the same affix rules) of the normal classes. @[code] class @AspectForAnyClass for ~ # To be applied to any classes; { var injected : list = {} # This is not a decorator! routine @AspectForAnyClass(){ io.writeln( 'In @AspectForAnyClass():' ); injected = { 1, 2, 3 } } # This decorator will also be applied to the default constructors: routine @DecoratorForAnyMethod( meth(args) : routine ) for ~ { io.writeln( 'In @DecoratorForAnyMethod():', std.about(meth) ) io.writeln( injected ) return meth( args, ... ) } } # For classes with names prefixed with My: class @AspectForMyClasses for My~ { routine @Method( meth(args) : routine ) for Method~ { io.writeln( 'In @AspectForMyClasses::@Method():', std.about(meth) ) return meth( args, ... ) } } class MyClass { routine Method(){ io.writeln( 'MyClass::Method()' ) } } k = MyClass() # Invoke the default constructor of Klass; k.Method() @[code] @[text] ################################################################################ ################################################################################ ######## Interface ################################################################################ ################################################################################ @[name] dao.tutorial.interface @[name] @[title] Abstract Interface @[title] @[text] Abstract interface is a type that describes how an object can be used, by specifying what methods and overloaded operators should be supported the object. An object is compatible (matching) to an interface type, if only if the object supports all the methods and operators that are specified by the interface. Interface is an abstract type, since no instance can be created from an interface, also all the methods of an interface are abstract without implementation. Here is a simple interface that contains a size checking method, @[code] interface HasSize { routine size()=>int } @[code] Now we can define a function that can take a parameter of any object that is compatible to this interface, @[code] routine PrintSize( object: HasSize ) { io.writeln( object.size() ) } @[code] Then this function can be called upon types such as @[green]string@[green], @[green]list@[green] or @[green]map@[green] etc. @[code] PrintSize( 'hello world' ) PrintSize( { 1, 2, 3 } ); @[code] Interface supports inheritance in the same way as class does, @[code] interface Resizable : HasSize { routine resize( size :int ) } @[code] Similarly, @[code] routine Resize( object: Resizable, size: int ) { io.writeln( 'old size:', object.size() ) io.writeln( 'new size:', size ) object.resize( size ) } ls = {} Resize( ls, 5 ) io.writeln( ls ) @[code] Interface also supports operator overloading, however, built-in operators for built-in types cannot be checked against an interface, because they are not implemented as methods. So interfaces are normally more useful with class instances and wrapped C/C++ types. Interfaces with the same set of abstract methods are interchangeable, @[code] interface HasSize2 { routine size()=>int } routine PrintSize2( object: HasSize2 ) { o :HasSize = object; # assign an object of "HasSize2" to a variable of "HasSize"; io.writeln( object.size() ) } PrintSize2( {} ); @[code] Just for testing, @[code(test)] interface HasSize { routine size()=>int } routine PrintSize( object: HasSize ) { io.writeln( object.size() ) } PrintSize( 'hello world' ) PrintSize( { 1, 2, 3 } ); interface Resizable : HasSize { routine resize( size :int ) } routine Resize( object: Resizable, size: int ) { io.writeln( 'old size:', object.size() ) io.writeln( 'new size:', size ) object.resize( size ) } ls = {} Resize( ls, 5 ) io.writeln( ls ) interface HasSize2 { routine size()=>int } routine PrintSize2( object: HasSize2 ) { o :HasSize = object; io.writeln( object.size() ) } PrintSize2( {} ); @[code(test)] @[text] ################################################################################ ################################################################################ ######## Exception/Panic Handling ################################################################################ ################################################################################ @[name] dao.tutorial.exception @[name] @[title] Exception Handling @[title] @[text] The handling of exceptions in Dao is very much like the handling of panics in the Go programming language. The basic idea is to implement exception handling codes in a function as deferred code block (see the @[green]Deferred Block@[green] subsection in @[node]dao.tutorial.controls@[node] for more information about this), which will be automatically executed when the function exists. In the deferred block, the built-in function @[green]recover()@[green] can be used to recover a list of exceptions, and handle them properly. Then the execution of the caller of function which defines the deferred blok will be resumed normally. @[code(dao)] routine Test() { defer { io.writeln( 'recovering:', recover() ) } io.writeln( 'Test(): before panic;' ) panic( 123 ) io.writeln( 'Test(): after panic;' ) } io.writeln( 'Before Test();' ) Test() io.writeln( 'After Test();' ) @[code(dao)] In this example, the built-in function @[green]panic()@[green] is used to raise an exception. This function can take any value as a parameter. If the parameter is an exception object, this exception will be raised; otherwise, an generic exception object will be created to hold the parameter value, and then the created exception object will be raised. Each function can define multiple deferred blocks, but only the call to @[green]recover()@[green] in the lastly executed deferred block will return the exceptions. An exception type can be passed to @[green]recover()@[green] to request a specific type of exception. @[code(dao)] # Example to handle user defined exception type: class MyError : Exception::Error { routine serialize(){ return ('MyError', self) } } routine Test() { defer { io.writeln( 'recovering from', recover( MyError ) ) } io.writeln( 'Test(): before panic;' ) panic( MyError() ); io.writeln( 'Test(): after panic;' ) } Test() @[code(dao)] As mentioned above, the normal execution will be resumed by the caller of the function that handles the exceptions in its deferred blocks. In order to resume the normal execution right after handling of exceptions, a special type of code blocks can be used to "host" the deferred blocks, @[code(dao)] frame { block } frame ( value ) { block } @[code(dao)] These kinds of blocks are executed in new stack frames, and when these blocks are finished and the frames are removed from the stack, deferred blocks encounted in these blocks will be automatically executed in the same way as the deferred blocks defined in normal functions. If the exceptions are recovered in these deferred blocks, the normal execution will be resumed right after these @[green]frame{}@[green] blocks, @[code(dao)] fout = io::stdio frame { defer { recover() } fout = io.open( "NonExistentFile.txt", 'r+' ) } if( fout != io::stdio ) defer{ fout.close() } fout.writeln( 'hello' ) @[code(dao)] Such @[green]frame{}@[green] blocks are actually expressions that may return values. The only difference between @[green]frame{block}@[green] and @[green]frame(value){block}@[green] is that, in the second case, if the @[cyan]block@[cyan] raises an exception, the default @[cyan]value@[cyan] is returned instead, and the exception is suppressed. This can make codes such as shown in the above example simpler, @[code(dao)] fout = frame( io::stdio ){ io.open( "NonExistentFile.txt", 'r+' ) } if( fout != io::stdio ) defer{ fout.close() } fout.writeln( 'hello' ) @[code(dao)] @[text] ################################################################################ ################################################################################ ######## Concurrent ################################################################################ ################################################################################ @[name] dao.tutorial.concurrent @[name] @[title] Concurrent Programming @[title] @[text] Dao has several features that support concurrent programming. The first is a concurrent Garbage Collector (GC), which works behind the scene. The other features come in the following forms: multi-threading module, asynchronous function call and asynchronous object, which are built around a lightweight thread pool with a simple scheduler to map thread tasks (or tasklets) to native threads. @[subsection] Multi-threading Module @[subsection] The Multi-threading Module (@[green]mt@[green]) offers the traditional multi-threading based on threads with mutexes, condition variables and semaphores. The main functionalities of @[green]mt@[green] are provided as code section methods for maximum flexibility and expressiveness. For example, a tasklet can be started with the @[green]mt.start()@[green] method, @[code] fut1 = mt.start { sum = 0; for( i = 1 : 10000 ) sum += i * i return sum } fut2 = mt.start( $now ) { sum = 0; for( i = 1 : 10000 ) sum += i * i return sum } io.writeln( fut1.value(), fut2.value() ) @[code] @[green]mt.start()@[green] can execute any expression or block of codes as a tasklet, which may be scheduled to run when a native thread become available to it. If the tasklet needs to be started immediately, the additional parameter @[cyan]$now@[cyan] can be passed to @[green]mt.start()@[green], so that a new native thread may be created if there is no other native thread available for the task. @[green]mt.start()@[green] returns a future value which will be used to store the value returned by the tasklet. Accessing its value by @[green]future.value()@[green] will cause the current tasklet to block until the value becomes available after the tasklet finishs. If one wants to wait for the task for only a limited amount of time, the method @[green]future.value()@[green] can be used with a timeout parameter (a float value for seconds or fraction seconds, negative for infinite waiting). @[subsection] Parallelized Code Section Methods @[subsection] However, the simplest way to do threaded programming is to use the following parallelized code section methods of @[green]mt@[green], @[list] -- @[green]iterate()@[green]: Iterate a predefined number of times, or iterate over a list, map or array, and execute the code block on each of the items; -- @[green]map()@[green] Map a list, map or array to another list, map or array, using the values returned by the code block; -- @[green]apply()@[green] Apply the values returned by the code block to a list, map or array; -- @[green]find()@[green] Find the first item in a list or map the satisfy the condition as tested by the code block. @[list] The default number of threads used by these methods are 2. Examples, @[code] ls = {1,2,3,4,5,6} # Use 2 threads to iterate 10 times: mt.iterate( 10 ) { io.writeln( X ) } # Use 3 threads to iterate over a list: mt.iterate( ls, 3 ) { io.writeln( X ) } mt.map( ls, 2 ) { X*X } # produces new list: {1,4,9,16,25,36} mt.apply( ls, 2 ) { X*X } # ls becomes: {1,4,9,16,25,36} @[code] @[subsection] Asynchronous Function Call @[subsection] Asynchronous Function Call (AFC) allows a function call to be executed as a tasklet, and return immediately a future value that can be use to block on the tasklet and wait for its completion. Any standard function call followed by @[green]!!@[green] will start an AFC. @[code] routine MyFunction( n = 10 ) { for( i = 1 : n ) io.writeln( i ) } f = MyFunction( 20 ) !! io.writeln( f.value() ) @[code] When multiple methods of a class instance are called in asynchronous mode, their execution will be mutually exclusive, namely, at any given time only call (tasklet) can be active. So in this way, the class instance acts just like a @[link]monitor@@http://en.wikipedia.org/wiki/Monitor_(synchronization)@[link]. @[subsection] Asynchronous Object @[subsection] An asynchronous object is a class instance that is created with asynchronous call mode, @[code] async_objet = some_class( ... ) !! @[code] When an asynchronous object invokes a method, it will be automatically called in asynchronous mode, and start a tasklet and return a future value. Such tasklets are queued and and executed in the order they are queued. So such asynchronous object acts like an actor in the @[link]actor model@@http://en.wikipedia.org/wiki/Actor_model@[link] considering that calling methods of such object is just like sending messages to the object with function parameters being the message contents. For each instance, these messages are processed one by one in the order of receiving. Here is an simple example, @[code] class Account { private var balance = 0 public routine Account( init = 0 ){ balance = init } routine Withdraw( amount : int ) => enum { if ( balance < amount ) return $false balance -= amount return $true } routine Deposit( amount : int ) => int { balance += amount return balance } routine Balance() => int { return balance } } acount1 = Account( 100 ) !! acount2 = Account( 100 ) !! future1 = acount1.Withdraw( 10 ) if( future1.value() == $true ) future2 = acount2.Deposit( 10 ) future3 = acount1.Deposit( 20 ) io.writeln( 'Balance in account1:', acount1.Balance().value() ) io.writeln( 'Balance in account2:', acount2.Balance().value() ) @[code] Like calling @[green]mt.start()@[green], calling a method on an asynchronous object will return a future value, which can be used to check the status of the asynchronous call. @[subsection] Communication Channels @[subsection] Tasklets (represented by future values) created from @[green]mt::start@[green], asynchronous function call and the methods of asynchronous object can be assigned to native threads to run concurrently. Tasklets can communicate and synchronize with each other using @[green]communication channels@[green]. The @[green]channel@[green] type is implemented as a customized C data type that supports template-like type arguments. So to construct a channel that can send and receive integers, one can use, @[code] chan = mt::channel() @[code] Here @[magenta]channel@[magenta] is prefixed with @[magenta]mt::@[magenta], because @[green]channel@[green] is defined in the built-in @[green]mt@[green] multi-threading module. The type argument @[magenta]int@[magenta] indicates this channel can only be used to transmit integers. Currently, only primitive types (@[green]int@[green], @[green]float@[green], @[green]double@[green], @[green]complex@[green], @[green]string@[green], @[green]enum@[green]) plus @[green]array@[green] types, and their composition through @[green]list@[green], @[green]map@[green] and @[green]tuple@[green] types are supported for channels. Each channel has a capacity for transmitting data, which are buffered in the channel. When the channel's buffer reached its capacity, any attempt to send data to it will result in the blocking of the sender, which will be resumed either after a timeout or after some data of the channel have been read out of the buffer to make room for the new data being sent by the blocked sender. Channel capacity can be passed to the constructor of the channel as an optional parameter, which is one by default. The essential methods of channel are: @[list] --send(): for sending data to the channel; --receive(): for receiving data from the channel; --select(): for selecting a group of channels; @[list] See @[node]dao.concurrent.channel@[node] for details. Here is a simple example, @[code] chan = mt::channel(2) produce = mt.start { index = 0; while( ++index <= 10 ){ io.writeln( 'sending', index ) chan.send( index ) } chan.cap(0) # set channel buffer size to zero to close the channel; } consume = mt.start { while(1){ data = chan.receive() io.writeln( "received", data ); if( data.status == $finished ) break } } @[code] @[text] ################################################################################ ################################################################################ ######## Tips ################################################################################ ################################################################################ @[name] dao.tutorial.tips @[name] @[title] Programming Tips @[title] @[text] @[section] Handling of command line arguments @[section] To handle command line arguments, one must define explicit main functions. With explicit main functions, the command line arguments will be parsed automatically according to the function parameters of these functions. For example, for a program to accept a number as command line argument, one can define the following main function, @[code(dao)] routine main( arg : double ) { io.writeln( arg ); } @[code(dao)] Then this script can be run in the following ways, @[code(dao)] shell$ dao script.dao 123 shell$ dao script.dao arg=123 shell$ dao script.dao -arg=123 shell$ dao script.dao -arg 123 shell$ dao script.dao --arg=123 shell$ dao script.dao --arg 123 @[code(dao)] As shown in the example, parameter names can appear in the command line arguments. In fact, in the command line arguments, @[cyan]arg=@[cyan], @[cyan]-arg=@[cyan] @[cyan]-arg@[cyan], @[cyan]--arg=@[cyan] and @[cyan]--arg=@[cyan] are always interpreted as argument names which must match to parameter names in main functions. Main functions can be overloaded with different parameter lists. The one with parameter names and types compatible with a given command line argument list will be executed. So user can define difine different main functions to handle different command line arguments. To allow the script to accept any command line arguments, one can define a main function with variadic parameter list, for example, @[code(dao)] routine main( ... as args ) { io.writeln( args ) } @[code(dao)] then all the command line arguments will be passed to this main function as a tuple with variable name @[cyan]args@[cyan]. @[text]