Укажите arity, используя только или исключая при импорте функции на Elixir

Я изучаю Elixir, и когда я использую операторы only или except при импорте функций из модуля, мне нужно указать номер arity. Почему?

например.

import :math, only: [sqrt: 1]

или

import :math, except: [sin: 1, cos: 1]

Ответ 1

Через экосистемные функции Erlang идентифицируются по имени + arity. В большинстве других языков вы можете перегружать функции по имени. Другими словами, в мире Erlang foo/1 (то есть foo (one_arg)) является совершенно другой функцией, чем foo/2 (как, например, foo (one_arg, two_arg)), но в Python или Ruby "foo" полное идентификатор функции и его можно вызвать с помощью гибкого числа аргументов.

Соглашение заключается в названии функций, которые означают одно и то же одно и то же имя, особенно в случае рекурсивно определенных итерационных функций, таких как:

factorial(N) -> factorial(1, N).

factorial(A, 0) -> A;
factorial(A, N) -> factorial(A * N, N - 1).

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

fac(N) -> sum(1, N).

sum(A, 0) -> A;
sum(A, N) -> sum(A * N, N - 1).

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

Соглашение состоит в том, чтобы называть связанные функции одинаковыми, но в действительности в экосистеме Erlang не допускаются функции перегрузки по arity. Для обеспечения приемлемости такой перегрузки потребуются значительные дополнения к компилятору языка, который компилируется в байт-код Erlang, и это будет бессмысленной тратой болезненных усилий. Текущая ситуация примерно так же хороша, как и на динамически типизированном функциональном языке (без того, чтобы он стал статически типизированным функциональным языком... и это еще одно обсуждение полностью).

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

Итак, когда вы import :math, only: [sqrt: 1] используете только math:sqrt/1 и оставите остальную часть модуля (был ли math:sqrt/2, вы проигнорировали бы его). Когда вы import :math, except: [sin: 1, cos: 1] берете все, кроме math:sin/1 и math:cos/1 (были ли math:sin/2, вы бы взяли его). Имя + arity - отличная идентичность. Представьте себе большой KV-магазин доступных функций. Клавиши {module, func, arity}, то есть они являются атомным значением для системы. Если вы знакомы с Erlang даже немного, это может показаться вам знакомым, потому что вы все время работаете с кортежем {Module, Function, Args}.

Ответ 2

Функции в Erlang и Elixir однозначно идентифицируются модулем /name/arity. Чтобы импортировать/исключать правильную функцию, вам необходимо указать все три части. Другой способ понять это - рассмотреть случай захвата ссылок на функции, например. &Map.get/2.

Даже если две функции имеют одно и то же имя, они фактически полностью разные функции для виртуальной машины. Чтобы ссылаться на правильный, вы должны правильно идентифицировать функцию, которую хотите вызвать, поэтому необходимо указать все три компонента с помощью only, except.