Recently, I was coding a module to create CSV files from database records when I came across an unexpected error in Elixir. Using the CSV library, I wrote something like this:

# mix.exs defp deps do [ { :csv , " ~> 1.4.2" } ... ] end # csv.ex defmodule Foo do defmodule CSV do def export ( file ) do CSV . encode ( file ) end end end

I expected Foo.CSV.export(file) to trigger the CSV library to encode the file I passed into the function. To my surprise, the app came back with this error:

( UndefinedFunctionError ) function Foo . CSV . encode / 1 is undefined or private

This error kind of makes sense. I’m calling a function on a CSV module, and I just defined a named CSV module right above it. Naturally, the app should be confused as to which CSV module I’m referring to. But I really didn’t want to change my module’s name, so how can I tell the app which module I want to use?

After some experimenting, I found something even more interesting - switching from a nested module to a single namespaced module made it work:

# csv.ex defmodule Foo . CSV do def export ( file ) do CSV . encode ( file ) end end

What?? I thought nested modules and namespaced modules compiled to the same thing? Shouldn’t the first example also compile to Foo.CSV ? And why does the second example not throw the same error as the first one?

Why this happens

After asking around, I found this behavior puzzled other Elixir developers as well. Thankfully, Bryan Joseph helped me figure out what was happening and also pointed me to part in the docs that explains this behavior.

While compiling, when Elixir reaches a nested module, it creates an “auto-alias” for that nested module. In other words, this code:

defmodule Foo do defmodule CSV do def export ( file ) do CSV . encode ( file ) end end end

actually compiles to something more like this behind the scenes:

defmodule Foo do defmodule Foo . CSV do def export ( file ) do CSV . encode ( file ) end end alias Foo . CSV , as: CSV end

This is why my original example threw an error - when CSV.encode(file) is called, the auto-alias directs the app to instead look for an encode/1 function on the Elixir.Foo.CSV module.

Yet, when modules are not nested, the compiler does not create an auto-alias, so there is no confusion about which CSV module I’m trying to reference.

So if you are hitting a similar error with nested modules, consider either changing the name or switching over to a namespaced module instead.