decorator!

Generating Macro

The decorator! macro is useful for creating higher-order functions, known as decorators, that can be used to modify or wrap classes, enums, methods, and parameters. This enables cross-cutting concerns like logging, validation, or persistence to be applied cleanly and declaratively.

Defining a Decorator

You define a decorator by providing different code blocks for the various kinds of things it can decorate. The macro generates a function that checks its arguments at runtime to figure out what it's decorating and executes the appropriate block.

local log = decorator! {
  -- A block for decorating class methods
  (_class, method) {
    local method_name = name -- a locally available variable
    print(f"Method '{method_name}' in class '{_class.__name}' is now being logged.")

    -- Return a new function that wraps the original method
    return function(...)
      print(f"Calling '{method_name}'...")
      local result = method(...)
      print(f"'{method_name}' returned.")
      return result
    end
  }
  -- A block for decorating enums
  (_enum) {
    print(f"Enum '{_enum.__name}' is now being logged.")
    -- You could wrap enum creation here, but for now we just log.
    return _enum
  }
}

Decorator Targets

The decorator! macro can target several different code constructs. You provide an implementation for each target you want to support.

  • _ { ... }
    • A common block executed for any decoration before the specific target block.
  • (_class) { ... }
    • Applied to a class definition. The _class variable holds the class table. You should return the (possibly modified) class table.
  • (_class, method) { ... }
    • Applied to a class method. _class is the class table and method is the function. You should return a new function.
  • (_self, value) { ... }
    • Applied to a parameter of a class constructor or method. _self is the class instance, and value is the parameter's value. You should return the (possibly modified) value.
  • (_enum) { ... }
    • Applied to an enum definition. _enum is the enum table. You should return the (possibly modified) enum table.
  • (_enum, variant) { ... }
    • Applied to an enum variant. _enum is the enum table and variant is the variant itself. This block can have its own sub-blocks:
      • _ { ... }: Common code for all variants.
      • static { ... }: Executed for simple, raw variants.
      • dynamic { ... }: Executed for variants that take arguments (function-like variants).

Using Decorators

You apply a decorator using the @ symbol before the item you want to decorate.

-- Using the 'log' decorator from above

enum! @log MyEnum, {
  A, B
}
-- Prints: Enum 'MyEnum' is now being logged.

class! MyClass, {
  my_method() {
    print("Executing my_method.")
  }
}

-- You can also apply decorators to methods directly
MyClass.my_method = log(MyClass, MyClass.my_method)
-- Prints: Method 'my_method' in class 'MyClass' is now being logged.

local instance = MyClass()
instance:my_method()
-- Prints:
-- Calling 'my_method'...
-- Executing my_method.
-- '{method_name}' returned.

Example for all of the decorator targets

Here is a simple example for all of the targets:

local deprecated = decorator! {
  (_class, method) {
    return function(...)
      print(f"Warning: Method '{name}' is deprecated.")
      return method(...)
    end
  }
  (_enum, variant) {
    dynamic {
      return function(...)
        print(f"Warning: Enum '{_enum.__name}' variant '{name}' is deprecated.")
        return variant(...)
      end
    }
    static {
      print(f"Warning: Enum '{_enum.__name}' variant '{name}' is deprecated.")
      return variant
    }
  }
  (_enum) {
    print(f"Warning: Enum '{name}' is deprecated.")
    return _enum
  }
  (_class) {
    function _class:init()
      print(f"Warning: Class '{name}' is deprecated.")
    end

    return _class
  }
  (_function) {
    return function(...)
      print(f"Warning: Function '{name}' is deprecated.")
      return _function(...)
    end
  }
  (_self, value) {
    print(f"Warning: Parameter '{name}' is deprecated.")
    return value
  }
}