Augments and Aspects

Augments | Aspects | Overlaying Augments

Augments

Augments are a way to add additional members and base types to a class separate from its primary definition - in other words, augments allow you to make distributed partial class definitions.

  • When you define an augment it is as if you had typed all that code into the original class definition.
  • Augments can only add new member definitions - they cannot redefine existing members (see aspects if you want to do that).
  • Augments can be defined before or after the main class.  They are applied in the order in which they are defined, with all the augments from the original file first, then all the augments from the first included file, and so on.
  • Augments are a useful way to implement basic separation of concerns - for example, they allow drawing logic to be defined independently of behavior logic, which in turn allows the drawing code to be cleanly swapped out.
  • See also: overlaying augments, below.

Examples:

class Hero
  method update:
    decide_what_to_do()
endClass

...

augment Hero
  method draw:
    draw_this_hero()
endAugment

# A Hero object now has two methods: update() and draw().

 

class Test
  method init:
    println( 6342.num_digits )  # prints: 4
endClass

# Add a new method to class Global
augment Global
  method num_digits( Int32 num, Int32 base=10 ).Int32:
    local Int32 count = 1
    num /= 10
    while (num?) ++count; num /= 10
    return count
endAugment

 

Aspects

Slag aspects are a unique approach to aspect-oriented programming and a powerful expansion of the idea of Java interfaces.

In simplest terms, an aspect is like a Java interface that can include method bodies and additional instance variables in its definition. Where Java implements interfaces, Slag incorporates aspects. During aspect incorporation, aspect method bodies are "cut & pasted" into existing methods of the same name. The syntax for controlling the order of this aspect layering is similar to that of making a prior (super-class) call.

Example:

class Test : IncreaseFormatValue, AddFormatBrackets
  METHODS
    method init:
      println( format(5) )  #prints out: <6>

    method format( Int32 n ).String:
      return "$" (n)
endClass

overlaying aspect IncreaseFormatValue
  METHODS
    method format( Int32 n ).String:
      return underlying( n + 1 )
endAspect

overlaying aspect AddFormatBrackets
  METHODS
    method format( Int32 n ).String:
      return "<$>" (underlying(n))
endAspect

Note that after the above example is compiled, there is a single format() method that is called once.

  • Each aspect should be declared as overlaying or underlying as a whole, which indicates the default order methods should be merged in. Individual methods can then be specified as being underlying or overlaying contrary to the default.
  • In the class declaration "class ExtendedClass : BaseClass, AspectAlpha, AspectBeta", methods are layered in the following order (the top one listed here is the bottom-most layer): 
    • All methods from BaseClass.
    • Underlying methods from AspectAlpha.
    • Underlying methods from AspectBeta.
    • All methods from ExtendedClass.
    • Overlaying methods from AspectAlpha.
    • Overlaying methods from AspectBeta.

      (Note that all "underlying" code references from underlying AspectAlpha methods would be adjusted to be "prior" calls instead)

  • Methods may reference "underlying" code even if there is no underlying code as long as no attempt is made to use an underlying return value. Such calls to non-existent code are silently removed.
  • Aspects can be used just like Java interfaces (where all methods are abstract) if desired. 
  • Object references can be of aspect types rather than class types. The same method-calling rules and restrictions apply as are found with Java interfaces.

 

Overlaying Augments

Augments are totally separate from a class but cannot redefine existing member definitions.  Aspects can redefine functionality at will but they must be specified as a base type for a class.  An overlaying augment is a way to combine the power of both.

  • An overlaying augment allows you to add and redefine behavior as an entirely independent definition.

Example:

# Modify class String to print a stack trace whenever substring() is called.
overlaying augment String
  method substring( Int32 i1, Int32 i2 ).String:
    println( "substring($,$)" (i1,i2) )
    return underlying(i1,i2)
endAugment


# The above code is shorthand for:
overlaying aspect AnonAspect2352
  method substring...
endAspect

augment String : AnonAspect2352;