Я изучаю Elixir, и когда я использую операторы only
или except
при импорте функций из модуля, мне нужно указать номер arity. Почему?
например.
import :math, only: [sqrt: 1]
или
import :math, except: [sin: 1, cos: 1]
Я изучаю Elixir, и когда я использую операторы only
или except
при импорте функций из модуля, мне нужно указать номер arity. Почему?
например.
import :math, only: [sqrt: 1]
или
import :math, except: [sin: 1, cos: 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}
.
Функции в Erlang и Elixir однозначно идентифицируются модулем /name/arity. Чтобы импортировать/исключать правильную функцию, вам необходимо указать все три части. Другой способ понять это - рассмотреть случай захвата ссылок на функции, например. &Map.get/2
.
Даже если две функции имеют одно и то же имя, они фактически полностью разные функции для виртуальной машины. Чтобы ссылаться на правильный, вы должны правильно идентифицировать функцию, которую хотите вызвать, поэтому необходимо указать все три компонента с помощью only
, except
.