Эликсир: есть ли преимущество использования & оператора для получения анонимной функции из именованной функции - PullRequest
0 голосов
/ 07 ноября 2018

Итак, я читал эту книгу, и в какой-то момент говорится, что:

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

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

Ответы [ 2 ]

0 голосов
/ 07 ноября 2018

Книга неверна. &MyModule.my_function/0 не не возвращает «анонимную функцию, которая вызывает данную функцию» - она ​​возвращает объект функции, который ссылается на названную функцию. Чтобы получить анонимную функцию, вы должны использовать fn -> MyModule.my_function() end.

Это становится важным, когда вы перезагружаете модули во время выполнения: анонимная функция является частью модуля, в котором она была создана, и поскольку на виртуальной машине хранятся только две версии данного модуля («текущая» и «старая») "версия), вы можете получить функцию, которую больше нельзя вызывать.

Давайте начнем с этого модуля:

defmodule MyModule do
  def the_real_function() do
    2
  end

  def anonymous_function() do
    fn -> __MODULE__.the_real_function() end
  end

  def named_function() do
    &__MODULE__.the_real_function/0
  end
end

Он содержит функцию, которая нам действительно интересна, и две функции, которые возвращают функции, которые ссылаются на реальную функцию - одну с анонимной функцией, другую с именованным объектом функции.

Давайте загрузим модуль, получим два функциональных объекта и проверим, что мы можем их вызвать:

iex(1)> c "my_module.ex"
[MyModule]
iex(2)> f_anon = MyModule.anonymous_function()
#Function<0.62651516/0 in MyModule.anonymous_function/0>
iex(3)> f_named = MyModule.named_function()
&MyModule.the_real_function/0
iex(4)> f_anon.()
2
iex(5)> f_named.()
2

Пока все хорошо. Теперь, скажем, нам нужно обновить модуль, чтобы функция возвращала 3. Мы редактируем модуль и перезагружаем его:

iex(6)> c "my_module.ex"
warning: redefining module MyModule (current version defined in memory)
  my_module.ex:1

[MyModule]
iex(7)> f_anon.()
3
iex(8)> f_named.()
3

Наши ссылки на функции все еще работают - хорошо. Скажем, нам нужно обновить модуль снова, чтобы вернуть 4:

iex(9)> c "my_module.ex"
warning: redefining module MyModule (current version defined in memory)
  my_module.ex:1

[MyModule]
iex(10)> f_anon.()
** (BadFunctionError) expected a function, got: #Function<0.62651516/0 in MyModule>

iex(10)> f_named.()
4

Теперь анонимная функция не работает! Это потому, что мы получили ссылку на него в двух версиях модуля, и поэтому код этой анонимной функции больше не хранится в виртуальной машине. Ссылка на именованную функцию все еще работает, потому что она просто ссылается на функцию с заданным модулем, именем и арностью.

0 голосов
/ 07 ноября 2018

Довольно часто есть такие простые функции, как

Enum.map(list, fn(element) -> element.id end)

Или просто

Enum.map(list, &(&1.id))

С некоторой практикой второе даже легче читать, чем первое

Относительно вашего вопроса, вы также можете вызвать именованную функцию таким же образом

Enum.map(list, &Integer.to_string/1)

Альтернативой является передача функции в Enum с помощью fn

Enum.map(list, fn(number) -> Integer.to_string(number) end)

Кроме того, захваченный вариант легче читать.

...