There are a lot of examples of using apply
within Elixir itself and popular libraries, if you want to study how it can be used and if it is consistent with your usage. This is from the internal implementation of DynamicSupervisor.start_child/2
defp start_child(m, f, a) do
try do
apply(m, f, a)
catch
kind, reason ->
{:error, exit_reason(kind, reason, __STACKTRACE__)}
else
{:ok, pid, extra} when is_pid(pid) -> {:ok, pid, extra}
{:ok, pid} when is_pid(pid) -> {:ok, pid}
:ignore -> :ignore
{:error, _} = error -> error
other -> {:error, other}
end
end
From GenServer.whereis/1
def whereis({:via, mod, name}) do
case apply(mod, :whereis_name, [name]) do
pid when is_pid(pid) -> pid
:undefined -> nil
end
end
Task.start_link/1
def start_link(fun) when is_function(fun, 0) do
start_link(:erlang, :apply, [fun, []])
end
Postgrex.SimpleConnection
, maintained by Elixir core members, I believe this is from an implementation of a behaviour:
def connect(_, %{state: {mod, mod_state}} = state) do
opts =
case Keyword.get(opts(mod), :configure) do
{module, fun, args} -> apply(module, fun, [opts(mod) | args])
fun when is_function(fun, 1) -> fun.(opts(mod))
nil -> opts(mod)
end
case Protocol.connect(opts) do
{:ok, protocol} ->
state = %{state | protocol: protocol}
with {:noreply, state, _} <- maybe_handle(mod, :handle_connect, [mod_state], state) do
{:ok, state}
end
{:error, reason} ->
if state.auto_reconnect do
{:backoff, state.reconnect_backoff, state}
else
{:stop, reason, state}
end
end
end
I feel like if you are using it with internal modules and functions then you are probably in the clear, but maybe there are gotchas I don’t know about. As @thiagomajesk said, if your args are unsafe then using apply
probably doesn’t make a difference.