Elixir - Call private function dynamically

At first, you should know that f() called inside a MyModule module is not the same thing as MyModule.f() called in the same place. See http://www.erlang.org/doc/reference_manual/code_loading.html#id86422

You can call private functions only f() style. These calls are also checked by the compiler - if the function does not exist, you get compile error. When you use MyModule.f() in the same place, you do not get compile error because these calls are checked at runtime only (even if you are calling the module from within itself) and the effect is (AFAIK) the same as if you were calling MyModule.f() from any other module - the module is looked up in runtime and you can call only exported (public) functions.

Therefore you can not call private functions in any other means than just a plain f(). apply(mod,fun,[]) is an equivalent to mod.fun.() style - the module is resolved at runtime and private functions are not accessible.

You can try all the variants by yourself in this example: https://gist.github.com/mprymek/3302ff9d13fb014b921b

You can see now that calls to private functions must always be known at the compilation time, so you can't use even eval_quoted magic or any other magic to make them "dynamic"...

Sasa Juric's advice to use @doc false is the right solution.


AFAIK dynamically invoking private functions is not possible in Erlang (and therefore not in Elixir). If you need to do a dynamic dispatch, you could consider using a multi-clause function. A contrived example (surely a bad one, but can't think of a better ATM):

iex(1)> defmodule Math do
          def operation(op) do
            IO.puts "invoking #{inspect op}"
            run_op(op)
          end

          defp run_op({:add, x, y}), do: x + y
          defp run_op({:mul, x, y}), do: x * y
          defp run_op({:square, x}), do: x * x
        end

iex(2)> Math.operation({:add, 1, 2})
invoking {:add, 1, 2}
3

iex(3)> Math.operation({:mul, 3, 4})
invoking {:mul, 3, 4}
12

iex(4)> Math.operation({:square, 2})
invoking {:square, 2}
4

Another alternative is to make your function public, but indicate with @doc false that they're internal - i.e. not meant to be used publicly by clients. You can also consider moving such functions to a separate module, and mark the whole module with @moduledoc false as internal. Both approaches are occasionally used in Elixir code.

I would however suggest starting simple, and use pattern matching + multi-clause functions. If the code becomes more complex, I would then consider other options.

Tags:

Elixir