16

Is there an equivalent for retrieving the name of a function just like like __MODULE__ retrieves the name of a Module in Elixir/Erlang?

Example:

defmodule Demo do
  def home_menu do
    module_name = __MODULE__
    func_name = :home_menu 
    # is there a __FUNCTION__
  end
End

EDITED

The selected answer works,

but calling the returned function name with apply/3 yields this error:

[error] %UndefinedFunctionError{arity: 4, exports: nil, function: :public_home, module: Demo, reason: nil}

I have a function :

defp public_home(u, m, msg, reset) do

end

The function in question will strictly be called within its module.

Is there a way to dynamically call a private function by name within its own module?

Charles Okwuagwu
  • 10,538
  • 16
  • 87
  • 157
  • 3
    [Predefined Macros](http://erlang.org/doc/reference_manual/macros.html#id85926) –  Nov 14 '17 at 08:42
  • @OnorioCatenacci I see your point. The function in question will strictly be called within its module. is there a way to dynamically call a private function by name within its own module? – Charles Okwuagwu Nov 14 '17 at 19:43
  • Are you recursively trying to call the function from within itself? In this particular case while I can see the appeal of not having to specify the function name, I'd say you should just bite the bullet and hard code it in the body of the function. – Onorio Catenacci Nov 14 '17 at 19:43
  • @OnorioCatenacci no. I store the previous and current function a user executes. I need to be able to use this to go back, and forward. Each method equates to a menu/page. I am trying to use apply to call the user.current or user.previous functions – Charles Okwuagwu Nov 14 '17 at 19:47
  • Then what about Code.eval_string? https://hexdocs.pm/elixir/Code.html#eval_string/3 Would that work for what you need? – Onorio Catenacci Nov 14 '17 at 19:51
  • i'll simply make the group of functions that need to be dynamically called public... simplest solution – Charles Okwuagwu Nov 14 '17 at 19:55

3 Answers3

29
▶ defmodule M, do: def m, do: __ENV__.function  
▶ M.m                                                 
#⇒ {:m, 0}

Essentially, __ENV__ structure contains everything you might need.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
4

Yes, there is. In Erlang there are several predefined macros that should be able to provide the information you need:

% The name of the current function
?FUNCTION_NAME

% The arity of the current function (remember name alone isn't enough to identify a function in Erlang/Elixir)
?FUNCTION_ARITY 

% The file name of the current module
?FILE 

% The line number of the current line
?LINE 

Source: http://erlang.org/doc/reference_manual/macros.html#id85926

Stratus3D
  • 4,648
  • 4
  • 35
  • 67
  • 1
    @do you know the elixir equivalent for each? – Charles Okwuagwu Nov 14 '17 at 15:00
  • 1
    No, but it looks like @mudasobwa's answer works in Elixir. `__ENV__.function` is the equivalent of `{?FUNCTION_NAME, ?FUNCTION_ARITY}`. This is one area where Elixir and Erlang differ quite a bit, so it's going to be hard to find something that works exactly the same between both of them. – Stratus3D Nov 14 '17 at 16:49
  • 1
    No, it's not: https://groups.google.com/d/msg/elixir-lang-talk/VbGTz7rKebM/f5Lml9R3Vx8J – PatNowak Nov 14 '17 at 16:50
2

To add to Aleksei's answer, here is an example of a macro, f_name(), that returns just the name of the function.

So if you use it inside a function, like this:

def my_very_important_function() do
   Logger.info("#{f_name()}: about to do important things")
   Logger.info("#{f_name()}: important things, all done")
end

you will get a log statement similar to this:

my_very_important_function: about to do important things
my_very_important_function: important things, all done

Details:

Here is the definition of the macro:

defmodule Helper do
  defmacro f_name() do
    elem(__CALLER__.function, 0)
  end
end

(__CALLER__ is just like __ENV__, but it's the environment of the caller.)

And here is how the macro can be used in a module:

defmodule ImportantCodes do
  require Logger
  import Helper, only: [f_name: 0]

  def my_very_important_function() do
     Logger.info("#{f_name()}: doing very important things here")
  end
end
theartofrain
  • 1,710
  • 15
  • 14