Meta(sploit)ing Elixir

The last exercise from had to do with meta programming Elixir in disguise. The task was an interesting one. You’re supposed to write a custom DSL that supports Graphviz like language for building graphs. So, one could write something like:

  graph [bgcolor="yellow"]
  a [color="red"]
  b [color="blue"]
  a -- b [color="green"]

and get back a graph structure like this:

Graph {
  attrs: [
    bgcolor: "yellow"
  nodes: [
    {:a, [color: "red"]},
    {:b, [color: "blue"]}
  edges: [
    {:a, :b, [color: "green"]}

Cool! Essentially it will transform “simpler” DSL language to Graph structure defined as:

defmodule Graph do
  defstruct attrs: [], nodes: [], edges: []

Now, if there was only something we could use to generate the desired graph structure, but without having to write support for every node name, type etc…

Elixir macros to the rescue! I tend to think about them as code that writes code, but for this case another aspect is far more important. Namely, when using macros, all the code that lands into macro is quoted. The official documentation offers some pretty detailed information, but it basically boils down to two simple facts:

  • code is represented as a tuple with specific structure that describes that code (that’s the “quote”)
  • the quoted / described code is not evaluated at that time

Now this has some pretty nice implications. E.g. you can inspect all the code prior to deciding what to do with it, but also it prevents side effects from discarded code. You become the master of all code 😀

Excellent, what now? Well, aside from quoted thingy protection, the tuple that quotes input code can be pattern matched. The structure is always:

{atom | tuple, list, list | atom}

Let’s see how that relates to our graph DSL problem space. We have the following sentences:

graph [attributes]
node [nodes]
node -- other_node [attributes]

You can inspect those in iex, e.g.:

quote do: c -- d [color: :blue]

This results with:

{:--, [context: Elixir, import: Kernel], [{:c, [], Elixir}, {:d, [], [[color: :blue]]}]}

As you can see, you can then create matching patterns like:

{:--, _, [{a, _, _}, {b, _, [attrs]}]}

This pattern would match any relation with attributes, e.g. a — b [color: :blue] or a — c [label: “meta!”]. This little neat iex trick helps a lot, you can basically take any code, quote it, and see what is it actually made of. Kinda like Matrix, right?!

OK, so now we have our macro, we get the quoted graph DSL expression as argument, and we can pattern match parts of that quoted DSL to generate the desired output. Let’s build that graph:

defp build_graph({:graph, _, [attrs]}) do
  quote do: %Graph{attrs: unquote(attrs)}

Two things happen here:

  • returning quoted result
  • unquoting values

With macros it’s quite straight forward. You receive quoted code as attributes and you need to return quoted result back. Hence the quote do: in the above example.

Excellent, but what is that unquote thing? Well, it’s a way to inject dynamic values into quoted expressions. You can think of it as evaluating quoted code and interpolating it in the result. In the above example we’ve pattern matched a value, the attributes of the graph, and then used the unquote to inject them into the resulting graph structure.

So, now you know. Let’s see a partial implementation of our graph DSL:

defmodule Dot do
  defmacro graph(do: ast) do

  defp build_graph({:__block__, _, graphs}) do
    |> |> build_graph |> Code.eval_quoted |> elem(0)))
    |> Enum.reduce(%Graph{}, &merge_graphs_sorted/2)
    |> Macro.escape

  defp build_graph({:--, _, [{a, _, _}, {b, _, [attrs]}]}) do
    quote do: %Graph{edges: [{unquote(a), unquote(b), unquote(attrs)}]}

  defp build_graph({:graph, _, [attrs]}) do
    quote do: %Graph{attrs: unquote(attrs)}

  defp build_graph({node, _, [keywords]}) do
    quote do: %Graph{nodes: [{unquote(node), unquote(keywords)}]}

  defp build_graph(_) do
    raise ArgumentError

Most of the code looks familiar, but there are a few tricks 🙂

defmacro graph(do: ast)

is just a simplified way to parse a passing block to the macro. Let’s compare the quoted input to macro, when invoking it with e.g. Dot.graph do a [color: :green] end:

  • [do: {:a, [line: 26], [[color: :green]]}] - using defmacro graph(ast)
  • {:a, [line: 26], [[color: :green]]} - using defmacro graph(do: ast)

The difference is obvious, we’re just shortening our macro implementation.

defp build_graph({:__block__, _, graphs}) do

is there for processing multiple lines of input to our graphs DSL. Essentially, Elixir tells us that a block has been passed in, that’s the :__block__ atom part. All the lines in the block are part of graphs pattern matched variable. As you can see in it’s implementation, we’re basically invoking build_graph per block line and merging the results.


is used to evaluate the build_graph result, which is quoted of course. We need the resulting graph structure, so we can merge it into the resulting graph (the reduce section).


is finally used to create quoted result, because that’s what we want to return from macro.

That is pretty much it. Working with macros is certainly not simple and follows it’s own rules. Even official documentation warns against abusing them too much, but they do have their place and certainly help with this kind of problems.

The complete example is available at or github as well.


Tagged , ,

One thought on “Meta(sploit)ing Elixir

  1. […] previous problem, we needed to merge two structs in a way that concatenates their list type […]

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: