ElixirSchool suggests you can use random capital word in Elixir as an "atom" (alias)... But is this a good idea to do in practice?

I note that if I write some lines like

        :mnesia.create_schema([node()])
        :mnesia.start() 
        :mnesia.create_table(TableName, [attributes: [:attribute_one, :attribute_two]])

This somehow works even though there is no module or variable of the name TableName anywhere before here. It is possible to continue to use this TableName just as written and access it this way going forward.

Similarly if you just type RandomWord into iex, nothing bad happens, it repeats it like an atom.

I see this was discussed a bit here, and it was stated that Elixir is treating the CapitalWord as an “Alias”

The documentation on Aliases suggests:

Aliases are constructs that expand to atoms at compile-time. The alias String expands to the atom :"Elixir.String". Aliases must start with an ASCII uppercase character which may be followed by any ASCII letter, number, or underscore. Non-ASCII characters are not supported in aliases.

Multiple aliases can be joined with ., such as MyApp.String, and it expands to the atom :"Elixir.MyApp.String". The dot is effectively part of the name but it can also be used for composition. If you define alias MyApp.Example, as: Example in your code, then Example will always expand to :"Elixir.MyApp.Example" and Example.String will expand to :"Elixir.MyApp.Example.String".

Elixir’s naming conventions recommend aliases to be in CamelCase format.

So my presumption is writing TableName then becomes :TableName in compilation.

But I also suspect this is not good code design and one should not use Aliases unless there is a specific purpose for it. Would you then agree?

My suspicion is you should call it either :table_name or :TableName and be clear about it, since you want an atom here clearly and nothing else.

Is there any particular naming convention that would be suggested for something like a table name in this case? Ie. Be explicit about the : or not? CamelCase or just stick to snake_case like every variable?

My inclination is :table_name.

ElixirSchool surprisingly recommends this alias method for table naming with Person in their example. But I am not sure it is “correct” here.

Thanks for any thoughts.

Ha, yes this is something that tripped me up for quite a while but I assure you it all makes sense (and is actually kinda cool).

Atoms always always ALWAYS start with :.

Tokens that start with a capital letter (ie, aliases), like TableName, are syntactic sugar.

TableName is not in fact short for :TableName but :"Elixir.TableName".

Try it out in iex:

iex(1)> TableName == :"Elixir.TableName"

Another neat test to help is:

iex(1)> :"TableName"
:TableName

(that was an alias just like :foo except that it happened to start with a capital letter)

then try:

iex(1)> :"Elixir.TableName"
TableName

Note there is no preceeding : since iex defaults to always showing the sugared version of things.

All modules in both Elixir and Erlang are referenced by atoms, but it’s totally fine to use those atoms as names for other things… because they are just atoms.

2 Likes

Almost, it is equivalent to :"Elixir.TableName" and perfectly reasonable to do. Such atoms are used to name processes without an accompanying module, for example.

1 Like

Here are some other examples to consider:

defmoulde :foo do
end

:point_up: totally legit. You’ve just defined a module that will trick unsuspecting people into thinking :foo is an OTP module.

defmodule :"Elixir.Foo" do
end

:point_up: Same as defmodule Foo, do: ()

You can also do this:

alias :mnesia, as: Mnesia

Again aliases are just atoms so that works. You can even do:

iex(1)> alias :foo, as: IDontExist
:foo
iex(2)> IDontExist
:foo

I feel like I’m lacking the exact correct language to use here but atoms are just names or tokens that can identify a module, but can also just be used as a name (when maybe you would otherwise use a string in other languages) to identify anything else. Usually they are used to name a global process and this is the big thing that tripped me up. Often when it comes to global GenServers, the convention is to name it after the module its callbacks are defined in which really makes it look like modules have state. But in reality, we’re just repurposing the atom used for the module to name the process. Ok haha, I feel I maybe be losing clarity here so I’ll stop but happy to dive deeper if you have questions.

Ugh, sorry for the three answers but I’m leaving out the most important thing that I was taking for granted:

Aliases always get prefixed with :"Elixir." if left out, ie, typing :"Elixir.Foo" is not going to create :"Elixir.Elixir.Foo". Aliases are not a thing in Erlang and this is what makes interop with Erlang so smooth… all built-in Elixir modules are prefixed with :"Elixir." and all OTP modules use the :atom version. This is of course convention for the standard library since, as demonstrated above, you can define your own :atom-style modules if you really want.

As above it’s syntax sugar. You can think about it like about Keyword.

iex> [a: 5] == [{:a, 5}]
true

There is nothing wrong with using both versions. :see_no_evil:


Again as above you can define module using both, but there is a noticeable difference when you look at which one is used by languages. So both are good to use, but Elixir one may be recommended to let a developer know that we work on Elixir part. :point_right:

For example often name for GenServer is __MODULE__ which expands to the alias of current module at compile time. Again you can use both, but when you see communication to GenServer with Example name then you know that it was most likely written in Elixir. :thinking:


Also since it’s well documented what aliases are then there is no worry that it would be expanded to for example String in future as this would break language’s API a lot. So even if there would be a plan for it then it would not be changed sooner than in 2.0.0 version. Again since it’s documented like that many libraries using metaprogramming depend on that feature and most likely this would prevent from such change. Even if it would happen in 2.0.0 or 3.0.0 then there would be definitely a support for old way i.e. defining :Elixir.Example by hand just to support old libraries. Since there are even no plans for 2.x releases most likely you would not see hard deprecation before AI would take all dev jobs. :smiley:


Aliases are and will be atom as same as Keyword is and will be list for a long, long time. There is no need to worry if using any is bad, but in specific cases you may prefer one over other. In this case there is no much difference except of simply showing what we work on (i.e. Elixir’s or Erlang’s named process). As always you have absolute freedom to work as you wish. You can define MyApp module in lib/everything_but_not_my_app_module.ex and it would still compile. :+1:

This is personal preference for but for non existent atoms of that style I prefer actually adding the module and a moduledoc to explain, for example, that it’s s process name for X.

I find it less confusing.

I’d like to turn this argument around. The BEAM has atoms and those are used to label various things on the VM due to them being light weight (integer mapped afaik) while being declared using text in code. Those labeled things are processes, ets tables, mnesia tables, modules, …. Hence atoms are not at all related to (just) modules.

When elixir was created erlang however already existed with lots of module names already in use. Hence it added the concept of an alias to namespace atoms to the elixir namespace without needing to write out that namespace all the time. Behind the scenes aliases still resolve to atoms as shown above. While aliases are certainly more prevalent to be used with modules and process labeling I would argue they’re just as loosely connected as other atoms.

4 Likes