The Pug Automatic

How to "expect(…).to" in Elixir

Written October 10, 2015. Tagged Elixir.

When I first came across ESpec, I was perplexed by syntax like

expect(pet).to be("Cat")

That doesn't look like Elixir!

It is in fact a controversial feature of Erlang, that may be removed in future versions of Elixir. So you probably shouldn't use it, but it can still be interesting to know how it works.

ESpec's expect function returns a tuple like {Expect, pet}, containing a module name and the argument. So we effectively have

{Expect, pet}.to be("Cat")

This, in turn, is interpreted as an Expect.to function call, with the tuple itself as the last argument:

Expect.to(be("Cat"), {Expect, pet})

This applies to any number of arguments. If we had done

{Expect, 1, 2, 3}.to(4, 5, 6)

then it would be interpreted as

Expect.to(4, 5, 6, {Expect, 1, 2, 3})

and so on.

How it all fits together

This is how you might implement a minimal version of expect(…).to:

defmodule Expect do
def to({:be, value}, {Expect, value}) do
IO.puts("Hooray, they're both '#{value}'!")
end

def to({:be, expected}, {Expect, actual}) do
IO.puts("Nay! Expected '#{expected}' but got '#{actual}' :(")
end
end

defmodule Example do
def run do
expect("Cat").to be("Cat")
expect("Cat").to be("Dog")
end

defp expect(actual) do
{Expect, actual}
end

defp be(expected) do
{:be, expected}
end
end

Example.run

Output:

Hooray, they're both 'Cat'!
Nay! Expected 'Dog' but got 'Cat' :(

If you want another example of how this might be used, see my ExMachina.with sketch.

What is this syntax?

This is Erlang tuple modules.

They are controversial in Erlang and Elixir both, and José Valim wants them gone in Elixir 2.0. Problems include hard-to-read stacktraces, slower dispatch, and that they can encourage writing code in an object-oriented style, with "methods" on "instances".