It breaks my heart that I have become so accustomed to using iex -S mix that I didn’t see this coming. I am trying to use Kernel.function_exported?(MyModule, function, arity) in an interactive session of iex -S mix. On the first run, it creates a build folder, and when I run function_exported?, it returns true.
The confusing part is that when I quit that session and run iex -S mix again, function_exported? returns false. It only returns true again after I delete the build folder and go through the process anew.
Why is that? I assume iex -S mix compiles and loads modules into the interactive session at any given moment?
Note that this function does not load the module in case it is not loaded. Check Code.ensure_loaded/1 for more information.
So you must always ensure the module is loaded before checking for function exported. It is not related to iex -S mix at all. It could also happen on mix test or mix phx.server.
Thanks @josevalim.
I am quite a noob. To clarify the documentation, when it says “in case it is not loaded,” Are we saying in my case, the module is not already loaded with the iex -S mix or am I missing something here?
Modules in the Erlang VM and Elixir are loaded dynamically, generally when you first invoke a function on them. For example, both URI and String are part of Elixir, but as soon as you start IEx, here is what you get:
There is nothing special about URI or String here, it just happens that something used String before (most likely IEx itself) but not URI.
So in your case, when you run iex -S mix and your module is available, it just happens that something loaded that particular application module. If it is available after the compiler runs, then the compiler was most likely the one who loaded it, but it does not matter, you are not supposed to expect that modules have been loaded. So you must check if they are loaded or not before.
Thanks for the privilege to ask more questions @josevalim
Is it a given or a certainty that when you start iex at any given instance, Code.loaded?(URI) returns false while Code.loaded?(String) returns true? I just tried it on my end, and I got the same output as you.
I think I clearly understand it now:
If I attempt to call a function from a module that hasn’t been explicitly loaded yet, Elixir will automatically load it the first time I call that function. This means that while the module might not be “loaded” in the sense of being in memory right away, it will be loaded when first used.
If I check Code.loaded?(MyModule) before I call a function, it might return false if the module hasn’t been accessed yet. However, once I call a function from that module, it gets loaded, and subsequent calls will reflect that.
It’s not a given, it’s a random artifact of your app’s specifics. The important thing to take away from this is that Elixir, by default, in non-production environments, does not proactively load all modules on startup. So in :dev and :test environments (on your machine or CI/CD workers) the modules are loaded as they’re needed. In :prod it’s the opposite. This behavior can be changed with a setting btw.