Hi all,
I’m trying to get up to speed with elixir and have just stumbled across a compiler warning that I find a bit puzzling. I was wondering if someone here could explain to me why the compiler behaves like this. Consider the following code:
defmodule Foo do
def f(), do: :foo
end
defmodule Bar do
def f(), do: :bar
end
defmodule Test do
def return_module_tuple_directly(name) do
case name do
"foo" -> {:ok, Foo}
"bar" -> {:ok, Bar}
_ -> {:error, :unknown_module}
end
end
def return_module_tuple_with_module_variable(name) do
module = case name do
"foo" -> Foo
"bar" -> Bar
_ -> nil
end
if module != nil, do: {:ok, module}, else: {:error, :unknown_module}
end
def this_works_fine(name) do
{:ok, module} = return_module_tuple_directly(name)
module.f()
end
def this_fails(name) do
{:ok, module} = return_module_tuple_with_module_variable(name)
module.f()
end
def this_also_fails(name) do
case return_module_tuple_with_module_variable(name) do
{:ok, module} -> module.f()
_ -> :error
end
end
def but_this_works(name) do
with {:ok, module} <- return_module_tuple_with_module_variable(name) do
module.f()
end
end
end
If I compile that, I’m getting the following warnings:
warning: nil.f/0 is undefined (module nil is not available or is yet to be defined)
│
34 │ module.f()
│ ~
│
└─ modules.ex:34:12: Test.this_fails/1
└─ modules.ex:39:31: Test.this_also_fails/1
What I don’t understand is:
-
In
this_fails/1, why does the compiler think the module could be nil? We’re explicitly testingif module != nil. So it should know that the module can never benilif the first tuple element is:ok. I’m assuming it’s smart enough to “see” that the value could benilfrom thecasestatement but then it’s not smart enough to also introspect the condition ofif(module != nil)? -
Why does wrapping the matching of
{:ok, module}in a with statement prevent this warning, but the similar variant with thecasestatement does not? This is what confuses me the most.
I’d really appreciate an explanation! Thanks!






















