14.2. Banged decorators

As explained in the decorators part Chapter 13, Decorators the following identity function:

function decorator =  |func| -> |x| -> func(x)

@decorator
function identity = |x| -> x

is expanded to:

function decorator =  |func| -> |x| -> func(x)

function identity = |x| -> decorator(|x| -> x)(x)

A banged decorator declared with the @! syntax:

function decorator =  |func| -> |x| -> func(x)

@!decorator
function identity = |x| -> x

is expanded to:

function decorator =  |func| -> |x| -> func(x)

function identity = |x| -> decorator!(|x| -> x)(x)

As seen previously, the decorator function is called only the first time. For every subsequent call, the function reference returned by the decorator is not re-computed but directly used as a constant.

Parametrized decorators can be banged too:

function decorator =  |arg| -> |func| -> |x| -> func(x)

@!decorator(42)
function identity = |x| -> x

is expanded to:

function decorator =  |arg| -> |func| -> |x| -> func(x)

function identity = |x| -> decorator(42)!(|x| -> x)(x)

Note

Considering the return of a banged call is constant, a common pitfall is to think that differents calls share the same "context" regardless where the call is located into the code.

As an example, consider two functions decorated with the same parametrized decorator:

@!deco("a")
function foo = |a| -> a

@!deco("b")
function bar = |b| -> b

These functions are expanded to

function foo = |a| -> deco("a")!(|a| -> a)(a)

function bar = |b| -> deco("b")!(|b| -> b)(b)

deco("a")!(|a| -> a) return a function that we can name for the example func_a, and deco("b")!(|b| -> b) return another function that we can name func_b.

Then, for every subsequent call of foo and bar, the executed code is somehow equivalent to:

function foo = |a| -> func_a(a)

function bar = |b| -> func_b(b)

func_a and func_b are now constant but different because they are not from the same "banged call instruction".

Performances can considerably increase with banged decorators, since the decorator function is no more called for each decorated function call. On the other hand, the decorator function has to be pure (without side-effects) and his parameters stable.