:books: Today I Learned

How to make an Elixir module alias itself

programming Elixir elixir Erlang erlang

Elixir modules are generally named with a hierarchical structure separated by dots:

defmodule MyProject.Thing do
  # ...
end

This is just a convention and provides no special functionality. Like Erlang modules, Elixir modules are just atoms, prefixed with "Elixir.":

Atom.to_string(List)  # "Elixir.List"
:"Elixir.List"        # List

Atom.to_string(MyProject.Thing)  # "Elixir.MyProject.Thing"

You can reference an Elixir module by its full name but that’s rather verbose and repetitive, especially in its own implementation. Here’s an example with an Elixir struct and a function that manipulate it:

defmodule MyProject.Thing do
  defstruct [:name, value: 0]

  def awesome(%MyProject.Thing{name: name} = thing) do
    %MyProject.Thing{thing | name: "Awesome #{name}"}
  end
end

Using __MODULE__

One way to avoid the repetition of the module name is to use the __MODULE__/0 macro from Kernel.SpecialForms. It returns the current module name as an atom:

defmodule MyProject.Thing do
  defstruct [:name, value: 0]

  def awesome(%__MODULE__{name: name} = thing) do
    %__MODULE__{thing | name: "Awesome #{name}"}
  end
end

Although the module name is no longer repeated, this may not be the most readable version.

Using an alias

Another way to avoid too much repetition is to use an alias:

defmodule MyProject.Thing do
  defstruct [:name, value: 0]

  alias MyProject.Thing, as: Thing

  def awesome(%Thing{name: name} = thing) do
    %Thing{thing | name: "Awesome #{name}"}
  end
end

This is a bit more readable.

Since aliases are often used as shortcuts for the last part of the module name like this, it is the default behavior if you don’t specify a name:

# These two aliases are equivalent:
alias MyProject.Thing
alias MyProject.Thing, as: Thing

You can also alias an Elixir module to whatever name you want if you prefer:

alias MyProject.Thing, as: Foo

Making a module alias itself

Ok, so an alias is good, but the solution above still makes you repeat the full module name twice. However, you can combine it with __MODULE__ and get the best of both worlds:

defmodule MyProject.Thing do
  defstruct [:name, value: 0]

  alias __MODULE__

  def awesome(%Thing{name: name} = thing) do
    %Thing{thing | name: "Awesome #{name}"}
  end
end

This way you both avoid the repetition of the full name and can use the more readable Thing instead of __MODULE__ in the rest of the code.

Credits

I learned this while reading chapter 2 of Functional Web Development with Elixir, OTP, and Phoenix, Rethink the Modern Web App by Lance Halvorsen.