Эликсир: использование против импорта

Какая разница между 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