Make compilation fail when undefined methods/modules are present

Hi,

I have a very specific question regarding the compilation. I have seen very annoying and ridiculous problems when it comes to maintainability, when code is not exactly compiled (As in Ruby, Python etc.).

Compile-ahead, I feel solves many problems and helps keep codebases sane and reliable. One of the reasons why I moved to Elixir is the compilation. But I found, it doesn’t strictly compile everything. For example, typos in files may be caught:

 defmodule Abc do
   def t do
     iakda
   end
 end

Compiling a file with the stuff above doesnt go through. However, something like this:

 defmodule Abc do
   def t do
     Haha.iakda
   end
 end

compiles with no problems, even though there is no module Haha.

 defmodule Haha do
 end
 defmodule Abc do
   def t do
     Haha.iakda
   end
 end

The above compiles too, there is a module called Haha but it doesn’t have the method lakda.

I would like to know if there’s a way to force the compiler to check undefined methods/modules and throw errors right away. This undeniably helps when refactoring large codebases.

Can someone throw some light if possible? Thank you!

Do you have warnings being emitted for undefined modules/functions? If so, you can use mix compile --warnings-as-errors.

1 Like

Well I came across that one but it also complains about deprecations which generally are mostly not critical

That’s entirely valid, it’s a function t in module Abc that is returning the atom Iakda. :slight_smile:

Still entirely valid, and that’s not a module, it’s still an atom. An atom is of the form of things like nil/true/false/:something/:"quoted something"/UpperCase.Dotted.Something and a few other styles. Something like Abc.Blah.t is the atom Abc.Blah being called the function t on it, nothing more. :slight_smile:

Iakda is not a function, it’s part of the atom Haha.Iakda. If you truly have a method named Iakda on module Haha then you would need to call it via apply or AST generation.

Well none of those are undefined methods or modules so I’m not sure how that could be checked for.

What it sounds like you want is a static type system, which I also fully want Elixir to have too. ^.^

Consequently, if you @spec’d up all the functions then dialyzer would complain at you on those anyway. :slight_smile:

2 Likes

Sorry, the iadka was somehow capitalized, but atoms was not what i meant, just some random methods that dont exist. In other words, I meant a method called “iakda”, not an atom called “Iakda”.

I fixed the code in the question now, would like to know your thoughts

It basically comes down to how dynamic the BEAM is. This SO answer highlights some of the reasoning behind it. But it essentially boils down to the fact that code can be loaded (and unloaded) at runtime, and may not exist at compile time. We also have the apply/3 function that can dynamically call functions at runtime, even if it is unknown at compile time.

2 Likes

Ah cool, I’ll go back over it then. :slight_smile:

Elixir uses a lower-case word as a binding name, thus it looks for a binding. As Elixir is a Lisp2 style language it differs the namespaces between local bindings and function bindings, and since this does not have a () to distinguish it between then it looks for a function local binding first, does not find one, then it looks for a local module binding (which if it found it would give you a warning about missing ()), but it doesn’t find that there so you get a compile failure.

There’s no module right ‘now’, doesn’t mean there won’t be a module later. The BEAM supports dynamical module loading, hot-code swapping, etc… etc… It’s impossible to know for sure whether a module will exist later by the time it is called or not.

That’s because that module is essentially forward declared to use a C’ism, just because it doesn’t have anything at compile-time doesn’t mean it won’t have anything at runtime by the time t is called because a replacement Haha module can be loaded by then, or one hot-swapped in at runtime that does have the function.

In other words Elixir is a very dynamic language with hot-code swapping, it’s impossible to know really anything at all outside of your own __MODULE__ at compile-time. :slight_smile:

2 Likes

Got it, thank you for a very detailed explanation. On the off hand,
mix xref
Seems to help find unnecessary and problematic code

2 Likes