defmodule Foo do
use DynamicBehaviour
@functions [:foo, :bar]
end
Expand to this…
defmodule Foo do
defmodule Behaviour do
@callback foo(term) :: :ok | {:error, term}
@callback bar(term) :: :ok | {:error, term}
end
@behaviour Foo.Behaviour
end
And I can’t get the metaprogramming quite right. This is what I have so far…
defmodule DynamicBehaviour do
defmacro __using__(_opts \\ []) do
quote do
@before_compile DynamicBehaviour
end
end
defmacro __before_compile__(_env) do
quote do
defmodule Behaviour do
Enum.each(@functions, fn function ->
@callback unquote(function)(term) :: :ok | {:error, | term}
end)
end
@behaviour Behaviour
end
end
end
But it’s not working. I understand that @functions does not exist in Foo.Behaviour, but in only Foo, but I’ve tried different things and still can’t get it to work. I kind of feel like it’s related to some kind of “double quoted” issue.
Not really, before compile does not simply append IIRC. Also, the functions you use there already uses its own @functions, as it’s inside a new module.
I’m not sure 100%, but perhaps this works:
quote do
functions = @functions
defmodule B do
Enum.each(functions, ...)
end
end
Even if I use a list literal, it still doesn’t work…
defmodule Behaviour do
Enum.each([:foo, :bar], fn function ->
@callback unquote(function)(term) :: :ok | {:error, | term}
end)
end
The error is on the @callback line and says:
variable "function" does not exist and is being expanded to "function()"
It kinda feels because I’m inside a quote, do: already, so the unquote is working on the outer scope instead of inside the each/defmodule. If that makes sense.
In other words… this works…
defmodule Foobar do
Enum.each([:foo, :bar], fn function ->
@callback unquote(function)(term) :: :ok | {:error, | term}
end)
end
But this doesn’t work…
quote do
defmodule Foobar do
Enum.each([:foo, :bar], fn function ->
@callback unquote(function)(term) :: :ok | {:error, | term}
end)
end
end
defmodule Foo do
Enum.each([:foo, :bar], fn name ->
@callback unquote(name)(term) :: :ok
end)
defmacro define_callbacks do
quote bind_quoted: [] do
Enum.each([:foo, :bar], fn name ->
@callback unquote(name)(term) :: :ok
end)
end
end
end
defmodule Bar do
@behaviour Foo
def foo("blah"), do: :ok
def bar("blah"), do: :ok
end
require Foo
defmodule Foo2 do
Foo.define_callbacks()
end
defmodule Bar2 do
@behaviour Foo2
def foo("blah"), do: :ok
def bar("blah"), do: :ok
end