    [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);