Note: This website is archived. For up-to-date information about D projects and development, please visit wiki.dlang.org.

Decorators

Decorators are a way to modify declarations. That sounds vague, but that's because they're not terribly complex or specialized, and because of that, you can use them to do a wide variety of things.

An example decorator is the attrs function included in the Base Library. This allows you to attach an "attribute table" to any arbitrary function, class, or namespace declaration, which can then be retrieved using the attributesOf function. You use it like so:

@attrs({
	foo = "bar"
	doc = "some documentation"
	[5] = 10
})
function blah()
{
	//...
}

The syntax is simple: @ followed by a function call, followed by the declaration.

How does the decorator do its job? Let's see.

What is a Decorator?

Well the previous section more or less explained what its purpose is, but what is it in terms of language features? A decorator is really just syntactic sugar for a function call with the declared class, function, or namespace becoming a parameter. The decorator itself (like attrs above) is nothing special, it's just any callable value - a function, a thread, a class, or an instance/table with an opCall metamethod.

When you use a decorator, the declared object becomes the first parameter to the decorator function. The decorator's return value is then used to replace the declared name. It's probably easier if I show you how it works. Consider the following code:

@deco(1, 2)
function foo() {}

Assuming this code is executed at module scope (i.e. foo is a global function), this becomes:

function foo() {}
foo = deco(foo, 1, 2)

Now you can see that the function has become the first parameter to the decorator, and the global foo is now being reassigned the return value of the decorator.

If you have multiple decorators, they become nested calls, with the last decorator being called first. So:

// if there are no params to a decorator, you can just leave off the parens.
@deco1
@deco2
function foo() {}

becomes:

function foo() {}
foo = deco1(deco2(foo))

Decorators can appear on class and namespace methods and fields. In these cases, the transformation is a little trickier, since these decorators might want to access the class or namespace in which they are being used. In order to allow this, an empty class or namespace is declared first, then its members are evaluated, and then any class or namespace decorators are evaluated:

@classDeco
class X
{
	@funcDeco(X)
	function foo() {}

	@varDeco
	fork = 4
}

Notice how funcDeco is being passed X. This becomes something like the following:

class X {} // empty, just here to declare the variable.
X.foo = funcDeco(function foo() {}, X) // now this access to X is legal
X.fork = varDeco(4)
X = classDeco(X) // evaluate class decorators last

Namespaces work identically.

Well! Now that you know probably more than you wanted to know about how decorators function, let's move on to what you can actually use them for.

Writing a Decorator

Let's start with the simplest possible decorator: one that does nothing at all. We'll call it the "identity" decorator.

function identity(x) = x

That's.. it. That's all there is. The first parameter to the decorator function is the object being decorated. The value you return will be used in place of the passed-in object. By returning the same object that was passed in, and by not modifying that object in any way, we do nothing.

We can now apply this decorator to anything like so:

@identity
function foo() {}

@identity
class Fork
{
	@identity
	function knife() {}
}

Whee, they all do nothing!

Okay, not that interesting. But pretty much all of your decorators will work basically the same way. You know everything you need to know.

Writing a Decorator that Does Something

This time, let's write a decorator that does something useful. Perhaps you'd like to keep track of how many times a function was called, for profiling or so. We could do that by keeping a table of functions and incrementing the count each time it was called. You can use a decorator to make this trivial.

{
	// private to the following function
	local callCounts = {}

	// note how we're using a parameter type constraint to restrict
	// what kinds of things this can decorate
	function countCalls(f: function)
	{
		// initialize the count for this function
		callCounts[f] = 0

		// we'll return a shim function to take its place
		return function(vararg)
		{
			// one more call
			callCounts[f]++

			// pass along 'this' and any arguments
			return f(with this, vararg)
		}
	}
	
	// just prints out the call counts table in a nice format
	function printCallCounts()
	{
		foreach(f; callCounts.keys().sort(\a, b -> nameOf(a) <=> nameOf(b)))
			writefln("{}: {} times", nameOf(f), callCounts[f])
	}
}

// this function will have its call count measured.  how easy!
@countCalls
function add(x, y) = x + y

// call it a bunch
for(i: 0 .. 100)
	add(i, i * 2)

// let's see how many times
printCallCounts()

Look how the countCalls decorator works: it takes a function, sets up an entry for it in the callCounts table, and then returns a shim function to replace the decorated function. Whenever the shim function is called, it increments the count and then transparently passes the arguments along to the original function. In this way the shim function can be used transparently the same way as the original function. The output of this code is:

add: 100

Decorating Classes

Writing a class decorator isn't all that different. Of course, you can modify a class's fields to do interesting things. Here's a simple decorator that adds a default toString method to print out the values of all the fields:

function autoToString(c: class)
{
	// add a toString method which will give something like "instance of Class { field1 = 4, field2 = 10 }"
	c.toString = function toString()
	{
		local ret = StringBuffer("instance of ")
		ret.append(nameOf(:super), " { ")

		local first = true

		foreach(fieldName, val; fieldsOf(this))
		{
			if(first)
				first = false
			else
				ret.append(", ")

			ret.format("{} = {}", fieldName, val)
		}

		ret.append(" }")

		return ret.toString()
	}

	// don't forget to return the class!
	return c
}

// using it
@autoToString
class A
{
	x
	y

	this(x, y)
	{
		:x = x
		:y = y
	}
}

// prints "instance of A { y = 5, x = 3 }"
writeln(A(3, 5))

Sometimes you'll want to do some more interesting member-specific things, where the member somehow "registers" itself in the class as being special. You could pass the class as a parameter to a per-member decorator, but that's tedious and error-prone. Instead, consider using the following idiom: make the member decorator just return a 'wrapper' object, then put a class decorator which reflects the fields and does something with those members which were decorated. Basically you're splitting the decorator in two.

As an example, consider a serialization system where you only want some fields of a class to be serialized while others are left alone. You can mark those you want (or don't want) to be serialized with a decorator, and then put a decorator on the class to indicate that the class itself is serializable.

// wrapper class which we will use as a decorator for fields that we want
// to be serialized.  all it does is save the value of the field.
class serial
{
	this(val)
		:val = val
}

// the class decorator that does all the work.
function serializable(c: class)
{
	// list of field names which should be serialized
	local serFields = []

	foreach(name, field; fieldsOf(c))
	{
		if(field as serial)
		{
			// ah, we've found a serializable field.  add it to serFields,
			// unwrap the value and put it back in the class.
			serFields ~= name
			c.(name) = field.val
		}
	}

	// add the list of serializable fields to the class.  I'm just using the
	// weird name to avoid name conflicts.
	c._serializeFields_ = serFields

	// let's just add a dummy serialize function.  it takes an output function which
	// accepts (name, value) pairs.
	c.serialize = function serialize(output: function)
	{
		foreach(fieldName; :_serializeFields_)
			output(fieldName, :(fieldName))
	}

	// REMEMBER THIS
	return c
}

// a serializable class!
@serializable
class Player
{
	// health and inventory should be saved..
	@serial health = 100
	@serial inventory

	// ..but the sprite that represents it should not.
	sprite

	this()
	{
		:inventory = []
	}

	function addItem(name: string)
		:inventory ~= name

	function hurt(amt: int)
		:health -= amt
}

// let's make a player!
local p = Player()
p.addItem("sword")
p.addItem("potion")
p.hurt(25) // it's super effective

// now we can serialize it.  we'll just print it out to the console.
p.serialize$ function(name, val)
	writefln("{}: {}", name, val)

This code outputs:

health: 75
inventory: ["sword", "potion"]

Cool.

You'll also notice in this example that serial is a class, though it's being used as a decorator. That's perfectly fine! A class is callable, and when you call it, you get an instance back.

Unusual Decorators

Most of the decorators I've shown so far have been functions. In the last example, I also showed a class being used as a decorator. As mentioned earlier, any callable type can be used as a decorator. You could use a thread object as a decorator. I'm not sure why! Maybe you could come up with something.

You could use a class instance or a table that has an opCall metamethod. The opCall becomes the decorator function. You might use this to keep track of state a little more cleanly than with a free decorator function. Here's a small, stupid example:

class Deco
{
	this() {}
	
	function opCall(f: function)
	{
		writeln("yay I'm decorating ", nameOf(f))
		return f
	}
}

global deco = Deco()

@deco
function blah() {}

The end

Some advanced topics appear in the next section.