Еліксир: використовувати проти імпорту


134

Яка різниця між useі import?

використання - це простий механізм використання заданого модуля в поточному контексті

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

Імпортує функцію та макроси з інших модулів

Здається, одна з різниць полягає importв тому, що ви виберете конкретні функції / макроси, тоді як useприносите все.

Чи є інші відмінності? Коли ви використовуєте одне над іншим?


Швидкий підсумок: import Moduleмістить функції, які будуть використовуватися всередині вашого модуля. use Moduleприносить функції, які слід використовувати, і відкриває їх публічно на вашому модулі
Джеред

Відповіді:


213

import Moduleприносить Moduleу ваш модуль всі функції та макроси, що не мають імен.

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

Як при used ModAвін згенерував importзаяву , яке було вставлено ModB.


6
Чудова відповідь! Для отримання додаткової інформації: elixir-lang.org/getting-started/alias-require-and-import.html
Джастіном

Потрапляючи в Еліксир і будучи зі світу Python, я ніби не розгублений у тому, що модулі є *.exфайлами та defmoduleблоками, і як ви витягнете модуль з файлу в iex REPL
Nick T

2
Спроба зрозуміти приклад / концепцію. У цьому конкретному випадку ви просто демонструєте, що __using__метод виконується на use ModA? Напевно, має сенс просто використовувати імпорт у ModBприкладі, який ви подали правильно?
Райан-Ніл Мес

35

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


1
Тож чи можна сказати use, що більш загальне, ніж import? Тобто функціональність import- це підмножинаuse
User314159

1
importпо - , як і раніше необхідно, так як я не знаю , якщо це правильно сказати , що ви можете перевизначити importз в useпоодинці, але я не здивуюся , якщо це можливо. useАле абсолютно потужніший. З нею ви можете робити дуже складні речі, наприклад, я дуже використовую useу своєму exprotobufпроекті, який ви можете перевірити, чи хочете ви, щоб це було висунуто до меж. Ви можете розширювати модулі з кодом, виконувати код під час компіляції, додавати функції до модуля тощо. В основному він поєднує в собі importі потужність макросів.
bitwalker

дякую за детальне пояснення та посилання на код. Я думаю, я зараз це зрозумію. Я все ще новачок у Elixir, але думаю, що коли я перегляну більше випадків використання, відмінності будуть очевидні.
Користувач314159

Привіт, не проблема, ще одним чудовим місцем для пошуку буде веб-рамка Phoenix. Кріс Маккорд написав книгу про макроси Elixir, і він активно використовує їх у Phoenix (у тому числі use). Для початківця це майже напевно простіше прочитати exprotobuf, але я думаю, що я, мабуть, підштовхую useдо його межі, exprotobufтак що це може бути корисним лише для того, щоб побачити, як далеко ви можете це взяти.
bitwalker

5
useнасправді багато не робить, він просто закликає __using__вказаний модуль.
Патрік Осіті

25

Перегляньте сторінку «псевдонім, вимагайте та імпортуйте» на офіційному посібнику з експлуатації 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(з числом 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

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). Але такої конвенції в Еліксирі немає, ви можете використовувати її на власний ризик.

У 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__()

Випробуваний на Еліксирі 1.5


3

use Module вимагає, Module а також дзвонить __using__на нього.

import Moduleприносить Moduleфункціональність у поточний контекст , а не просто вимагає цього.


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 є більше інструкцій щодо обробки залежностей .

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.