Is it common to use `use` and `__using__` for re-dispatching?

In Phoenix apps, the web module provides this “re-dispatch into methods” technique for use (see __using__):

defmodule ExampleWeb.Router do
  use ExampleWeb, :router
end

defmodule ExampleWeb.TodoController do
  use ExampleWeb, :controller
end

defmodule ExampleWeb.Layouts do
  use ExampleWeb, :html
end

defmodule ExampleWeb do
  @doc """
  When used, dispatch to the appropriate controller/live_view/etc.
  """
  defmacro __using__(which) when is_atom(which) do
    apply(__MODULE__, which, [])
  end

  def router do
    #
  end

  def controller do
    quote do
      # ...
    end
  end

  def html do
    quote do
      # ...
    end
  end
end

What was the goal/benefit behind doing it that way? I’m guessing it’s for cutting on boilerplate, as otherwise it would’ve been:

defmodule ExampleWeb.TodoController do
  use ExampleWeb.Controller
end

defmodule ExampleWeb.Layouts do
  use ExampleWeb.HTML
end

defmodule ExampleWeb.Router do
  # hmm, probably inlined because same module name
end

defmodule ExampleWeb.Controller do
  defmacro __using__ do
    # ...
  end
end


defmodule ExampleWeb.HTML do
  defmacro __using__ do
    # ...
  end
end

Do we see more examples of this technique being used in libraries and projects besides Phoenix?

I’m trying to grasp the philosophy behind every decision :sweat_smile:

I think it’s rather an edge case especially to make code “shorter” and have a single “helper” module for this. It’s most probably because this code is not a part of a hex package, but a simple and short not documented (private functions have no documentation) part of your codebase. They are short and contains just a few aliases/imports and stuff like that. I would say it would be divided in few modules only if the inside logic would be more complex than what we have right now.

The entire ExampleWeb module logic is just so simple that it can’t compare to the logic of a typical macro. Also I believe it’s easier to maintain such helper in a single file rather than working on multiple files. The current Phoenix version have already impressive structure and does not need a bunch of tiny files just to separate such a simple logic.

4 Likes

Thank you! Interestingly, even Phoenix 1.0 from 2015 already shipped with the same web module, while the pre-release version from 2014 didn’t have it and used the alternative “separate modules” approach.