How to call a method dynamically in Elixir, by specifying both module and method name?

20,631

You can use apply/3 which is just a wrapper around :erlang.apply/3. It simply invokes the given function from the module with an array of arguments. Since you are passing arguments as the module and function names you can use variables.

apply(:lists, :nth, [1, [1,2,3]])
apply(module_name, method_name, [1, array])

If you want to understand more about how elixir handles function calls (and everything else) you should take a look at quote and unquote.

contents = quote do: unquote(module_name).unquote(method_name)(1, unquote(array))

which returns the homoiconic representation of the function call.

{{:.,0,[:lists,:nth]},0,[1,[1,2,3]]}

You can unquote the quoted function call with Code.eval_quoted/3

{value, binding} = Code.eval_quoted(contents)

Edit: here is an example using Enum.fetch along with a var.

quoted_fetch = quote do: Enum.fetch([1,2,3], var!(item));             
{value, binding} = Code.eval_quoted(quoted_fetch, [item: 2])
Share:
20,631

Related videos on Youtube

halfelf
Author by

halfelf

Hardcore PC/Board/TRPG Gamer, Poet, Ruby/Erlang/Clojure Fan. Now works at http://www.nautiluscapital.net My start up: https://aimeike.tv https://duomu.tv

Updated on July 09, 2022

Comments

  • halfelf
    halfelf over 1 year

    I'd like to know what exactly a method name is in elixir:

    array = [1,2,3]
    module_name = :lists
    method_name = :nth                  # this not working
    module_name.method_name(1, array)   # error, undef function lists.method_name/2
    module_name.nth(1, array)           # returns 1, module_name is OK. It's an atom
    

    But I can do almost the same thing in erlang:

    A = [1,2,3].
    X = lists.
    Y = nth.
    X:Y(1,A).  #  returns 1
    

    How can I do this in elixir?

  • halfelf
    halfelf over 11 years
    Good. So the method name is a atom. Now I think it is just the syntax which don't allow us writing module.method in elixir, right?
  • lastcanal
    lastcanal over 11 years
    I believe you are correct. I think the only way to make this work would be to change the syntax to use atoms when calling module functions (i.e. :lists.:nth). I'd rather just use apply in cases like this.
  • halfelf
    halfelf over 11 years
    Thanks. This answer is very helpful.
  • CMCDragonkai
    CMCDragonkai over 9 years
    Can you show how this could be done for a normal elixir function. Something like Enum.fetch? Is there variable variables or variable functions?
  • lastcanal
    lastcanal over 9 years
    quoted_fetch = quote do: Enum.fetch([1,2,3], var!(item)); {value, binding} = Code.eval_quoted(quoted_fetch, [item: 2])
  • lastcanal
    lastcanal over 9 years
    I've updated the answer for better formatting. Using var!/1 allows you to substitute a variable inside a quoted method for the value of the corresponding key in the binding.