Эликсир: использование против импорта
Какая разница между use
и import
?
http://elixir-lang.org/docs/stable/elixir/Kernel.html#use/2
use - простой механизм для использования данного модуля в текущем контексте
http://elixir-lang.org/docs/stable/elixir/Kernel.SpecialForms.html#import/2
Импортирует функцию и макросы из других модулей
Похоже, что одно отличие: import
позволяет вам выделять конкретные функции/макросы, тогда как use
включает все.
Существуют ли другие отличия? Когда вы будете использовать один над другим?
Ответы
Ответ 1
import Module
добавляет все функции и макросы Module
un-namespaced в ваш модуль.
require Module
позволяет использовать макросы Module
, но не импортирует их. (Функции Module
всегда доступны для имен.)
use Module
сначала requires
, а затем вызывает макрос __using__
на Module
.
Рассмотрим следующее:
defmodule ModA do
defmacro __using__(_opts) do
IO.puts "You are USING ModA"
end
def moda() do
IO.puts "Inside ModA"
end
end
defmodule ModB do
use ModA
def modb() do
IO.puts "Inside ModB"
moda() # <- ModA was not imported, this function doesn't exist
end
end
Это не будет компилироваться, поскольку ModA.moda()
не был импортирован в ModB
.
Далее будет компилироваться следующее:
defmodule ModA do
defmacro __using__(_opts) do
IO.puts "You are USING ModA"
quote do # <--
import ModA # <--
end # <--
end
def moda() do
IO.puts "Inside ModA"
end
end
defmodule ModB do
use ModA
def modb() do
IO.puts "Inside ModB"
moda() # <-- all good now
end
end
Как и при use
d ModA
, он сгенерировал оператор import
, который был вставлен в ModB
.
Ответ 2
use
предназначен для ввода кода в текущий модуль, а import
используется для импорта функций для использования. Вы можете создать реализацию use
, которая автоматически импортирует функции, например, я использую Timex, когда вы добавляете use Timex
в модуль, посмотрите на timex.ex, если хотите чтобы понять, что я имею в виду, это очень простой пример построения модуля, который может быть use
'd
Ответ 3
Обратитесь к странице "alias, require and import" из официального руководства по началу работы с elixir:
# Ensure the module is compiled and available (usually for macros)
require Foo
# Import functions from Foo so they can be called without the `Foo.` prefix
import Foo
# Invokes the custom code defined in Foo as an extension point
use Foo
Требовать
Elixir предоставляет макросы как механизм метапрограммирования (написание кода, который генерирует код).
Макросы - это куски кода, которые выполняются и расширяются во время компиляции. Это означает, что для использования макроса нам необходимо гарантировать, что его модуль и реализация доступны во время компиляции. Это делается с помощью директивы require
.
Как правило, модуль не требуется перед использованием, кроме случаев, когда мы хотим использовать макросы, доступные в этом модуле.
Импорт
Мы используем import
всякий раз, когда хотим легко получить доступ к функциям или макросам из других модулей, не используя полностью квалифицированное имя. Например, если мы хотим несколько раз использовать функцию duplicate/2
из модуля List
, мы можем ее импортировать:
iex> import List, only: [duplicate: 2]
List
iex> duplicate :ok, 3
[:ok, :ok, :ok]
В этом случае мы импортируем только функцию duplicate
(с arity 2) из List
.
Обратите внимание, что import
модуль автоматически require
.
Использование
Хотя это не директива, use
- это макрос, тесно связанный с require
, который позволяет вам использовать модуль в текущем контексте. Макрос use
часто используется разработчиками для приведения внешних функциональных возможностей в текущую лексическую область, часто модули.
За кулисами use
требуется данный модуль, а затем вызывает обратный вызов __using__/1
, позволяющий модулю вставлять некоторый код в текущий контекст. Вообще говоря, следующий модуль:
defmodule Example do
use Feature, option: :value
end
скомпилирован в
defmodule Example do
require Feature
Feature.__using__(option: :value)
end
Ответ 4
use Module
требует Module
, а также вызывает на нем __using__
.
import Module
добавляет функциональность Module
в текущий контекст, а не просто требует.
Ответ 5
С фоном из языков Python/Java/Golang, import
vs use
также был смущен для меня. Это объяснит механизм повторного использования кода с примерами некоторых декларативных языков.
импорт
Короче говоря, в Elixir вам не нужно импортировать модули. Доступ ко всем публичным функциям возможен с помощью полнофункционального синтаксиса MODULE.FUNCTION:
iex()> Integer.mod(5, 2)
1
iex()> String.trim(" Hello Elixir ")
"Hello Elixir"
В Python/Java/Golang вам нужно import MODULE
, прежде чем вы сможете использовать функции в этом модуле, например Python
In []: import math
In []: math.sqrt(100)
Out[]: 10.0
Тогда что import
в Elixir может вас удивить:
Мы используем импорт, когда хотим легко получить доступ к функциям или макросам из других модулей, не используя полностью квалифицированное имя
https://elixir-lang.org/getting-started/alias-require-and-import.html#import
Итак, если вы хотите набрать sqrt
вместо Integer.sqrt
, trim
вместо String.trim
, import
поможет
iex()> import Integer
Integer
iex()> sqrt(100)
10.0
iex()> import String
String
iex()> trim(" Hello Elixir ")
"Hello Elixir"
Это может вызвать проблемы при чтении кода и при конфликте имен, поэтому не рекомендуется в Erlang (язык, который влияет на Elixir). Но в Эликсире такого соглашения нет, вы можете использовать его на свой страх и риск.
В Python такой же эффект можно сделать:
from math import *
и рекомендуется использовать в некоторых специальных сценариях/интерактивный режим - для более короткого/быстрого ввода текста.
использовать и требовать
Что отличает use
/require
, так это то, что они относятся к "макро" - концепции, которая не существует в семействе Python/Java/Golang...
Вам не нужно import
использовать модуль, чтобы использовать его функции, но вам нужно require
модуль использовать его макросы:
iex()> Integer.mod(5, 3) # mod is a function
2
iex()> Integer.is_even(42)
** (CompileError) iex:3: you must require Integer before invoking the macro Integer.is_even/1
(elixir) src/elixir_dispatch.erl:97: :elixir_dispatch.dispatch_require/6
iex()> require Integer
Integer
iex()> Integer.is_even(42) # is_even is a macro
true
Хотя is_even
может быть записан как нормальная функция, это макрос, потому что:
В Elixir Integer.is_odd/1 определяется как макрос, так что его можно использовать в качестве защитника.
https://elixir-lang.org/getting-started/alias-require-and-import.html#require
use
, к выдержке из документа Elixir:
Для использования требуется данный модуль, а затем вызывает обратный вызов __using__/1
, позволяющий модулю вставлять некоторый код в текущий контекст.
defmodule Example do
use Feature, option: :value
end
скомпилирован в
defmodule Example do
require Feature
Feature.__using__(option: :value)
end
https://elixir-lang.org/getting-started/alias-require-and-import.html#use
Таким образом, запись use X
аналогична записи
require X
X.__using__()
use/2
- макрос, макрос преобразует код в другой код для вас.
Вам нужно use MODULE
, если вы:
- хотите получить доступ к своим макросам (
require
)
- И выполните
MODULE.__using__()
Протестировано на Elixir 1.5