Another option would be to generate all the required code into the module that says use. This replaces threading callback_module everywhere by local calls:
defmodule MyBehaviour do
@callback specific_handling(data :: term) :: term
@callback very_specific_handling(data :: term) :: term
defmacro __using__(_opts) do
quote do
@behaviour MyBehaviour
alias MyBehaviour
def initiate_processing(data) do
data
|> do_stuff_with_data()
# and other calls to behaviour-functions
|> specific_handling()
end
defp do_stuff_with_data(data) do
data
# and other calls to behaviour-functions
|> do_specific_stuff()
end
defp do_specific_stuff(data) do
data
# and other calls to behaviour-functions
|> very_specific_handling()
end
end
end
end
defmodule DataHandler11 do
use MyBehaviour
def specific_handling(data) do
# do specific stuff with data
data
end
def very_specific_handling(data) do
# do specific stuff with data
data
end
end
defmodule DataHandler12 do
use MyBehaviour
def specific_handling(data) do
# do specific stuff with data
data
end
def very_specific_handling(data) do
# do specific stuff with data
data
end
end
data = nil
handler = 1
case handler do
1 -> DataHandler11.initiate_processing(data)
2 -> DataHandler12.initiate_processing(data)
end
This has a cost: the code output by __using__ is compiled over again for every module that calls it, versus only being compiled once when MyBehaviour is compiled.






















