1
votes

I have a macro that puts catch-all functions in the end of the module, so resulting module looks something like:

defmodule Module1 do
   <module body>
   ## Generated catch-all functions
   def fun1(_, _), do: :ok
   ## ..more catch-all functions...##
end ## of module

The problem I'm trying to solve is to suppress warnings

"this clause cannot match because a previous clause at line XX always matches" in case the user of macro will have, for instance

   def fun1(arg1, arg2), do: ...

in the body of the module.

I guess I can go through module's body AST and do some analysis of function signatures before generating catch-all functions, but it seems to be a lot of work. Is there any other way?

1
Can we step back and ask why you are defining catch-all clauses automatically? Remember explicit is almost always better than implicit.José Valim
My use case is a generation of a module with with about 10 callbacks. it's a SAX parser that handles XML events, switching handlers as the state of the system the parser is being used within changes. Each handler is only supposed to handle certain SAX events, described by the creator of the module, all other events being handled by default handler. It's the same boilerplate code for all the handlers, so I thought it'd make sense to generate it. It looks like GenServer is doing something similar, as it generates catch-alls for all gen_server callbacks, but lets the user override them.Boris Okner
Yeah, I think the GenServer behaviour is the sanest thing you can do. Provide default implementations but, if you override it, you need provide the catch all explicitly. What do you think?José Valim
Well, in case of GenServer I still want to keep the catch-all clause that was generated in the first place, just making it the last clause of the callback. def MyServer do use GenServer end { :ok, pid } = GenServer.start_link(MyServer, 100) send pid, :hello :hello , but: def MyServer do use GenServer def handle_info(:my_msg, state) do IO.puts :my_msg {:noreply, state} end end { :ok, pid } = GenServer.start_link(MyServer, 100) send pid, :hello I'll get "no function clause" exceptionBoris Okner
Sorry, can't get formatting right :-(Boris Okner

1 Answers

1
votes

(I think you know this solution but maybe it could be useful to other SO users to answer this question :) )

As José have said, it's not a very good practice to hide too much in the code as it can lead to surprises (hidden code can do something you do not expect t to do) and can even limit you (in this particular case, if you have un-explicit catchall, you can't get rid of it...).

I thing the best approach is to explicitly use a macro to generate all the boilerplate code. One line to call the macro is not as much boilerplate ;)

Example implementation: https://gist.github.com/mprymek/73f878e103f60d6d89e2