I’ve got a wrapper module that I am using around a library. The purpose of this module is to add small behavior to the library without outright forking it. I have a macro that produces the appropriate defdelegate
and defoverridable
calls on all of the functions that I need from library but I find myself writing a ton of boilerplate that could probably also be in the macro.
I have a struct called LibraryWrapper
that looks like this:
defstruct type: Library.new(...)
For 1 arity functions I have a generalization that looks like this:
alias LibraryWrapper, as: LW
def foo(%LW{type: type}), do: Library.foo(type)
defdelegate foo(type), to: Library
For 2 arity functions, I have a generalization that looks like this:
alias LibraryWrapper, as: LW
def bar(%LW{type: type_a}, %LW{type: type_b}), do: Library.bar(type_a, type_b)
def bar(type_a, %LW{type: type_b}), do: Library.bar(type_a, type_b)
def bar(%LW{type: type_a}, type_b), do: Library.bar(type_a, type_b)
defdelegate(type_a, type_b), to: Library
If there were higher arity functions, the pattern would continue (but there aren’t for this particular library). Heres what the macro I am using looks like currently; and as you will see the defdelegate
calls inside of it are completely irrelevant because the overridden functions override these making it so that they will never match. But if I could add a general pattern for all arity functions before the defdelegate
call then I could just use the macro and not write all of the boilerplate.
defmacro wrap(module) do
module= Macro.expand(module, __CALLER__)
functions = module.__info__(:functions)
sigs =
Enum.map(functions, fn {name, arity} ->
args =
if arity == 0 do
[]
else
Enum.map(1..arity, fn i ->
{String.to_atom(<<?A + i - 1>>), [], nil}
end)
end
{name, [], args}
end)
combined = List.zip([sigs, functions])
for comb <- combined do
quote do
defdelegate unquote(elem(comb, 0)), to: unquote(module)
defdelegate unquote([elem(comb, 1)])
end
end
end
Where I am running into issues is trying to figure out how to build the AST for getting the field out of the struct that I plan to pass into the macro. Also, I am not entirely sure how to build AST where the function matches on the specific struct type as I am doing above. I also have to wonder if there is a better way to do this apart from making a struct wrapper.