Reputation: 1022
I have a protocol that has a single function with a relatively complex API:
defprotocol Foo do
def complex(foo, x, y)
end
I want to provide a way to implement this protocol for a common and simpler use case. After some experimentation, I came up with the following:
defmodule Bar do
@callback simple(any, any) :: boolean
defmacro __using__(_) do
quote do
@behaviour Bar
defimpl Foo do
# TODO this is really hacky. What is a better way to reference parent module?
# Note that the 1 in drop(1) is from Foo's module name
@parent_module __MODULE__ |> Module.split |> Enum.drop(1) |> Module.concat
def complex(bar, x, _y) do
matches = @parent_module.simple(bar, x)
if matches, do: [x], else: []
end
end
end
end
end
Now I can implement Foo via use Bar
, but the way that @parent_module
is determined and used seems wrong. Is there a better way than using the hack in the TODO
above? What is the idiomatic way to do this? I would rather not turn Foo
into a behaviour because dispatch and consolidation of protocols fits the usage pattern.
Upvotes: 2
Views: 705
Reputation: 222128
You can get and store the parent module's name in a local variable, then put it in an attribute in the implementation and then use it:
defmodule Bar do
@callback simple(any, any) :: boolean
defmacro __using__(_) do
quote do
@behaviour Bar
parent_module = __MODULE__
defimpl Foo do
@parent_module parent_module
def complex(bar, x, _y) do
matches = @parent_module.simple(bar, x)
if matches, do: [x], else: []
end
end
end
end
end
Upvotes: 4