[NAME]
ALL.dao.routine.decorator

[TITLE]
Function Decorator

[DESCRIPTION]

 Note: this feature is only functional in the online demos and the latest devel 
release.

Decorators are a special type of functions that can be used to enhance the
functionalities of other functions by decorating them, which may either modify these
functions or create a modified version of these functions.

There are different ways to decorate a function, but only functions that are decorated at
their defintion points are modified. In all other cases, function decoration always
produces new and modified copies of the original functions.

A decorator is almost identical to a noraml function except that it must be declared with
a name prefixed with @, and 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.

 0.1   Decorator for Any Functions  

If a decorator will be used to decorate any functions, the routine type for the first
parameter of the decorator can be declared as the most generic function type routine

Here is a simple decorator that can decorate any function.
     
   1  routine @Decorator( func(args) : routine )
   2  {
   3      io.writeln( 'Calling function:', std.about(func) );
   4      return func( args, ... ); # ... for parameter expanding;
   5  }
     


Decorators can be applied at the places where functions are defined.
     
   1  @Decorator()
   2  routine Function(){ io.writeln( 'Function()' ); }
     


If the decorator does not take any parameter other than the function to be decorated,the
brackets can be omitted:
     
   1  @Decorator
   2  routine Function( a : int ){ io.writeln( 'Function(int)', a ); }
   3  
   4  Function();
   5  Function(123);
     


 0.2   Decorator for Specific Function Type  

A decorator can be defined such that it can only be applied to a specific type of
functions, if the type of such functions is specified as the first parameter of the
decorator.

Here is a deocrator that can only be applied to functions that accepts an integer as
parameter and returns a string. For a decorator for testing such as this one, the
expected output can be passed as additional parameters to the decorator:
     
   1  routine @TestDecorator( func(args) : routine<index:int=>string>, expected = '' )
   2  {
   3      res = func( args, ... );
   4      io.writeln( res );
   5      io.writeln( 'Test result:', res == expected ? 'passed' : 'failed' );
   6      return res;
   7  }
   8  
   9  @TestDecorator( 'Hello' )
  10  routine Hello( index = 0 )
  11  {
  12      io.writeln( 'Calling Hello(int)' );
  13      return 'Hello';
  14  }
  15  
  16  Hello();
     


 0.3   Overloaded Decorator  

Decorators can be overloaded just like the normal functions.

Here is an overloaded deocrator that can only be applied to functions that accepts a
string as parameter and returns an integer.
     
   1  routine @TestDecorator( func(args) : routine<name:string=>int>, expected = 0 )
   2  {
   3      res = func( args, ... );
   4      io.writeln( res );
   5      io.writeln( 'Test result:', res == expected ? 'passed' : 'failed' );
   6      return res;
   7  }
     


# Decorators can be chained:
     
   1  @Decorator
   2  @TestDecorator( 123 )
   3  routine Hello( name : string )
   4  {
   5      io.writeln( 'Calling Hello(string)', name );
   6      return 123;
   7  }
   8  
   9  io.writeln( Hello( 'abc' ) );
     


 0.4   Decorator in Expression  

Decorator can be used in the same way as normal functions, namely calling a decorator
like a normal function, and pass the function to be decorated and other values as
parameters to the decorator.
     
   1  anotherHello = @Decorator( Hello )
     
Using decorator in this way will produce a modified copy of the function.

If all the parameters passed to a decorator called in constant form (namely not used
through a varaible), this expression of calling the decorator is a constant expression
and can be evaluated at compiling time.
     
   1  const Hello3 = @Decorator( Hello )
   2  Hello3( 'def' )
     

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.
     
   1  load MyModule  # MyFunction is defined in this module;
   2  
   3  # Create a modified copy and use the same name:
   4  const MyFunction = @MyDecorator( MyFunction )
     


When a decorator is called in non-constant expression, the decoration will be done at
running time,
     
   1  routine Hello2( index = 123 )
   2  {
   3      io.writeln( 'Calling Hello2(int)', index );
   4      return 'Hello';
   5  }
   6  # Running time decoration:
   7  func = @TestDecorator( Hello2, 'Hello' );
   8  io.writeln( '--------' )
   9  func(123)
  10  
  11  func = @TestDecorator( func );
  12  io.writeln( '--------' )
  13  func(123)
     


Just like normal functions, decorators can be assigned to variables and used at running
time:
     
   1  deco = @Decorator;
   2  func = deco( func );
   3  
   4  io.writeln( '--------' )
   5  func(789);
     


 0.5   Decorator for Class Method  

Decorators can be applied to class methods. Such decorators can be defined outside of the
class as well as inside the class as a class member.

If the routine type of the first parameter of a decorator does not contain a self
parameter, this decorator can be applied to static methods,
     
   1  routine @StaticDecorator( meth(args) :routine<id:int=>int>, value = 123 )
   2  {
   3      io.writeln( args, value );
   4      args.id = value;
   5      return meth( args, ... );
   6  }
     
Otherwise, it can be applied to instance methods,
     
   1  routine @MethodDecorator( meth(args) :routine<self:@T,id:int=>int>, value = 123 )
   2  {
   3      io.writeln( args, value );
   4      args.id = value;
   5      return meth( args, ... );
   6  }
     


When a decorator is defined as a class method, it must be declared as a static method,
because its first parameter must be a routine type,
     
   1  class Klass
   2  {
   3      static routine @ClassDecorator( meth(args) :routine<id:string=>?>, value = 'abc' ){
   4          io.writeln( args, value );
   5          args.id = value;
   6          return meth( args, ... );
   7      }
   8  
   9      # Decorator can be applied to class methods:
  10      @StaticDecorator( 456 )
  11      static routine StaticMeth( id :int ){ io.writeln( id ); return id }
  12  
  13      @ClassDecorator
  14      static routine StaticMeth( id :string ){ io.writeln( id ) }
  15  
  16      @MethodDecorator( 789 )
  17      routine Meth( id :int ){ io.writeln( id ); return id }
  18  }
  19  
  20  Klass::StaticMeth( 0 );
  21  Klass::StaticMeth( 'a' );
  22  
  23  k = Klass();
  24  k.Meth(1);