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.
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.
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".