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:
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:
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:
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:
actually compiles to something more like this behind the scenes:
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
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.