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

135

Какая разница между useа import?

use - это простой механизм использования данного модуля в текущем контексте

https://hexdocs.pm/elixir/Kernel.SpecialForms.html#import/2

Импортирует функции и макросы из других модулей

Похоже, одно различие заключается в importтом, что вы можете выбрать конкретные функции / макросы, а useвводить все.

Есть ли другие различия? Когда бы вы использовали один поверх другого?

User314159
источник
Краткое резюме: import Moduleвводит функции, которые будут использоваться внутри вашего модуля. use Moduleвводит функции для использования И публикует их в вашем модуле
Jered

Ответы:

213

import Moduleпереносит все функции и макросы Moduleun-namespace в ваш модуль.

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сделали, ModAон сгенерировал importоператор, который был вставлен в ModB.

greggreg
источник
6
Отличный ответ! Для получения дополнительной информации: elixir-lang.org/getting-started/alias-require-and-import.html
Джастином
Зайдя в Elixir и будучи из мира Python, я немного запутался в том, что модули являются *.exфайлами и defmoduleблоками, и как вы извлекаете модуль из файла в реплику IEX
Ник Т.
2
Пытаюсь понять пример / концепцию. В этом конкретном случае вы просто демонстрируете, что __using__метод выполняется на use ModA? Вероятно, имеет смысл просто использовать импорт в ModBприведенном вами примере, правильно?
Райан-Нил Мес,
35

useпредназначен для внедрения кода в текущий модуль, в то время importкак используется для импорта функций для использования. Вы можете создать useреализацию, которая, например, автоматически импортирует функции, как я делаю с Timex, когда вы добавляете use Timexв модуль, взгляните на timex.ex, если вы хотите знать, что я имею в виду , это очень простой пример того, как создать модуль , который может быть use«д

bitwalker
источник
1
Так можно ли сказать, что useэто более общий, чем import? То есть функциональность importявляется подмножествомuse
User314159
1
importпо - прежнему необходимо, так как я не знаю , если это правильно сказать , что вы можете переопределить importс в useодиночку, но я не удивлюсь , если это возможно. useхотя и абсолютно более мощный. Вы можете делать с ним очень сложные вещи, например, я активно использую его useв своем exprotobufпроекте, который вы можете проверить, если хотите, чтобы он был доведен до предела. Вы можете расширять модули с помощью кода, выполнять код во время компиляции, добавлять функции в модуль и т. Д. В основном он сочетает в себе importи возможности макросов.
bitwalker
спасибо за подробное объяснение и ссылки на код. Я думаю, я понял это сейчас. Я все еще новичок в Elixir, но думаю, что если я посмотрю на другие варианты использования, различия станут очевидны.
User314159
Эй, нет проблем, еще одно отличное место для поиска - это веб-инфраструктура Phoenix. Крис МакКорд написал книгу о макросах Elixir, и он активно использует их в Phoenix (в том числе use). Его почти наверняка будет легче читать для новичка exprotobuf, но я думаю, что я, вероятно, достигну useэтого предела, exprotobufпоэтому может быть полезно просто посмотреть, как далеко вы можете зайти.
bitwalker
5
useна самом деле мало что делает, просто вызывает __using__указанный модуль.
Патрик Оссити,
25

Смотрите страницу «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

требовать

Эликсир предоставляет макросы как механизм для метапрограммирования (написания кода, генерирующего код).

Макросы - это фрагменты кода, которые выполняются и раскрываются во время компиляции. Это означает, что для использования макроса мы должны гарантировать, что его модуль и реализация доступны во время компиляции. Это делается с помощью requireдирективы.

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

Импортировать

Мы используем, importкогда хотим легко получить доступ к функциям или макросам из других модулей без использования полного имени. Например, если мы хотим использовать duplicate/2функцию из Listмодуля несколько раз, мы можем импортировать ее:

iex> import List, only: [duplicate: 2]
List
iex> duplicate :ok, 3
[:ok, :ok, :ok]

В этом случае мы импортируем только функцию duplicate(с арностью 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
fetsh
источник
14

Имея опыт работы с языками Python / Java / Golang, importvs 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). Но в 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:

use требует данный модуль, а затем вызывает __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

HVNSweeting
источник
3

use Module требует Module и также призывает __using__к этому.

import Moduleпривносит Moduleфункциональность в текущий контекст , а не просто требует ее.

хаги-tragger
источник
0

Импортировать

Делает все функции и макросы из данного модуля доступными внутри лексической области, в которой он вызывается. Имейте в виду, что в большинстве случаев вам нужно импортировать только одну или несколько функций / макросов.

Пример:

defmodule TextPrinter do
  import IO, only: [puts: 1]

  def execute(text) do
    puts(text)
  end
end

iex> TextPrinter.execute("Hello")
Hello
:ok

использование

Этот макрос позволяет вам внедрить любой код в текущий модуль. Вы должны быть осторожны при использовании внешних библиотек с use, поскольку вы можете не знать , что именно происходит за кулисами.

Пример:

defmodule Printer do
  defmacro __using__(_opts) do
    quote do
      def execute(text) do
        IO.puts(text)
      end
    end
  end
end

defmodule TextPrinter do
  use Printer
end

iex> TextPrinter.execute("Hello")
Hello
:ok

Код сцены внутри __using__был вставлен в TextPrinterмодуль.

Кстати, в Elixir больше инструкций по работе с зависимостями .

szsoppa
источник