Есть ли способ использовать динамическое c имя функции в Elixir из интерполяции строк, как в Ruby? - PullRequest
1 голос
/ 07 апреля 2020

Я хочу иметь возможность создавать вызов функции из строки в эликсире. Это возможно? Эквивалентный ruby вызов метода будет:

"uppercase".send("u#{:pcase}")

Ответы [ 2 ]

4 голосов
/ 08 апреля 2020

Хотя ответ @fhdhsni совершенно верен, я бы добавил некоторые уточнения.

Точный эквивалент Kernel#send из в невозможно, потому что Kernel#send позволяет вызывать private методы на получателе. В частные функции никогда не существуют в скомпилированном коде.

Если вы имели в виду Kernel#public_send, это может быть достигнуто с помощью Kernel.apply/3, как упоминалось @fhdhsni. Единственное исправление состоит в том, что таблица атомов не является сборщиком мусора, и, безусловно, нужно вызвать действительно существующую функцию , это следует сделать с помощью String.to_existing_atom/1.

apply(
  String,
  String.to_existing_atom("u#{:pcase}"),
  ["uppercase"]
)

Кроме того, можно использовать макросы на этапе компиляции для генерации соответствующих предложений, когда список вызываемых функций предсказуем (в противном случае код уже пахнет).

defmodule Helper do
  Enum.each(~w|upcase|a, fn fname ->
    def unquote(fname)(param),
      do: String.unquote(fname)(param)
    # or
    # defdelegate unquote(fname)(param), to: String 
  end)
end
Helper.upcase("uppercase")
#⇒ "UPPERCASE"
1 голос
/ 07 апреля 2020

В Elixir имена модулей и функций - это атомы. Вы можете использовать apply, чтобы вызывать их динамически.

apply(String, String.to_atom("u#{:pcase}"), ["uppercase"]) # "UPPERCASE"

В зависимости от вашего варианта использования может быть плохой идеей динамическое создание атомов (так как таблица атомов не является сборщиком мусора).

...