Elixir: użyj a import

139

Jaka jest różnica między usei import?

use to prosty mechanizm wykorzystania danego modułu w aktualnym kontekście

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

Importuje funkcje i makra z innych modułów

Wygląda na to, że jedna różnica polega importna tym, że możesz wybrać określone funkcje / makra, a jednocześnie usewprowadzić wszystko.

Czy są inne różnice? Kiedy użyłbyś jednego nad drugim?

User314159
źródło
Szybkie podsumowanie: import Modulezawiera funkcje do wykorzystania w module. use Modulewprowadza funkcje do użycia ORAZ ujawnia je publicznie w Twoim module
Jered

Odpowiedzi:

216

import Modulewprowadza Moduledo modułu wszystkie funkcje i makra z przestrzeni bez nazw.

require Moduleumożliwia korzystanie z makr programu, Moduleale ich nie importuje. (Funkcje Modulesą zawsze dostępne w przestrzeni nazw).

use Modulepierwszy requiresmoduł, a następnie wywołuje __using__makro Module.

Rozważ następujące:

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

Nie zostanie to skompilowane, ponieważ ModA.moda()nie zostało zaimportowane do programu ModB.

Jednak skompilowane zostaną następujące elementy:

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

Jak wtedy, gdy usebyłyby ModAto wygenerował importkomunikatu, który został wstawiony ModB.

greggreg
źródło
6
Świetna odpowiedź! Więcej informacji: elixir-lang.org/getting-started/alias-require-and-import.html
justin
Wchodząc do Elixiru i będąc ze świata Pythona, jestem trochę zdezorientowany, że moduły są *.explikami i defmoduleblokami oraz w jaki sposób wyciągnąłbyś moduł z pliku do REPL iex
Nick T
2
Próba zrozumienia przykładu / koncepcji. W tym konkretnym przypadku po prostu demonstrujesz, że __using__metoda jest wykonywana na use ModA? Prawdopodobnie miałoby sens po prostu użycie importu ModBw przedstawionym przykładzie, prawda?
Ryan-Neal Mes
35

usejest przeznaczony do wstrzykiwania kodu do bieżącego modułu, podczas gdy importsłuży do importowania funkcji do użycia. Możesz zbudować useimplementację, która automatycznie importuje funkcje, na przykład tak, jak robię to z Timex, kiedy dodajesz use Timexdo modułu, spójrz na timex.ex, jeśli chcesz wiedzieć, co mam na myśli , to bardzo prosty przykład, jak zbudować moduł, który może być use'd

bitwalker
źródło
1
Czy zatem słuszne jest stwierdzenie, że usejest bardziej ogólne niż import? Oznacza to, że funkcjonalność importjest podzbioremuse
User314159
1
importnadal jest konieczne, ponieważ nie wiem, czy jest to dokładne powiedzieć można reimplement importz usesamodzielnie, ale nie zdziw się, jeśli to możliwe. usejest jednak absolutnie potężniejszy. Możesz z nim robić bardzo złożone rzeczy, na przykład ja intensywnie wykorzystuję je usew moim exprotobufprojekcie, które możesz sprawdzić, jeśli chcesz zobaczyć, jak przesuwa się do granic możliwości. Możesz rozszerzać moduły o kod, wykonywać kod w czasie kompilacji, dodawać funkcje do modułu itp. Zasadniczo łączy to importi moc makr.
bitwalker
dzięki za szczegółowe wyjaśnienie i odniesienia do kodu. Myślę, że teraz rozumiem. Wciąż jestem nowy w Elixirze, ale myślę, że kiedy przyjrzę się większej liczbie przypadków użycia, różnice będą oczywiste.
User314159
Hej, nie ma problemu, kolejnym świetnym miejscem do obejrzenia byłby framework sieciowy Phoenix. Chris McCord napisał książkę o makrach Elixir i używa ich intensywnie w Phoenix (w tym use). Prawie na pewno będzie łatwiejsze do przeczytania dla początkującego niż exprotobuf, ale myślę, że prawdopodobnie dążę usedo jego granic, exprotobufwięc może być przydatne po prostu zobaczyć, jak daleko możesz to znieść.
bitwalker
5
usew rzeczywistości niewiele robi, po prostu wywołuje __using__określony moduł.
Patrick Oscity,
25

Zobacz stronę „alias, require i import” w oficjalnym przewodniku wprowadzającym do elixiru:

# 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

Wymagać

Elixir dostarcza makra jako mechanizm do metaprogramowania (pisania kodu, który generuje kod).

Makra to fragmenty kodu, które są wykonywane i rozwijane w czasie kompilacji. Oznacza to, że aby korzystać z makra, musimy zagwarantować, że jego moduł i implementacja będą dostępne podczas kompilacji. Odbywa się to za pomocą requiredyrektywy.

Ogólnie moduł nie musi być wymagany przed użyciem, chyba że chcemy użyć makr dostępnych w tym module.

Import

Używamy importzawsze, gdy chcemy mieć łatwy dostęp do funkcji lub makr z innych modułów bez używania w pełni kwalifikowanej nazwy. Przykładowo, jeśli chcemy kilkakrotnie skorzystać z duplicate/2funkcji z Listmodułu, możemy ją zaimportować:

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

W tym przypadku importujemy tylko funkcję duplicate(z liczbą 2) z List.

Zwróć uwagę, że importmoduł automatycznie requirego obsługuje.

Posługiwać się

Chociaż nie jest to dyrektywa, usejest ściśle powiązanym makrem, requirektóre pozwala na użycie modułu w bieżącym kontekście. useMakro jest często wykorzystywany przez programistów przynieść funkcjonalność zewnętrznego do bieżącego zakresu leksykalnego, często modułów.

W tle usewymaga danego modułu, a następnie wywołuje __using__/1wywołanie zwrotne umożliwiające modułowi wstrzyknięcie kodu do bieżącego kontekstu. Ogólnie rzecz biorąc, następujący moduł:

defmodule Example do
  use Feature, option: :value
end

jest wkompilowany w

defmodule Example do
  require Feature
  Feature.__using__(option: :value)
end
fetsh
źródło
14

Mając tło z języków Python / Java / Golang, importvs usebył dla mnie zdezorientowany. To wyjaśni mechanizm ponownego użycia kodu z kilkoma przykładami języków deklaratywnych.

import

Krótko mówiąc, w Elixirze nie musisz importować modułów. Dostęp do wszystkich funkcji publicznych można uzyskać poprzez w pełni kwalifikowany MODUŁ. Składnia FUNKCJI:

iex()> Integer.mod(5, 2)
1

iex()> String.trim(" Hello Elixir  ")
"Hello Elixir"

W Python / Java / Golang musisz to zrobić import MODULEzanim będziesz mógł używać funkcji w tym MODULE, np. Python

In []: import math

In []: math.sqrt(100)
Out[]: 10.0

W takim razie to, co importrobi Elixir, może Cię zaskoczyć:

Korzystamy z importu, gdy chcemy mieć łatwy dostęp do funkcji lub makr z innych modułów bez używania w pełni kwalifikowanej nazwy

https://elixir-lang.org/getting-started/alias-require-and-import.html#import

Więc jeśli chcesz pisać sqrtzamiast Integer.sqrt, trimzamiast String.trim, importpomoże

iex()> import Integer
Integer
iex()> sqrt(100)
10.0

iex()> import String
String
iex()> trim(" Hello Elixir    ")
"Hello Elixir"

Może to powodować problemy z czytaniem kodu i konfliktami nazw, więc nie jest to zalecane w Erlangu (języku, który ma wpływ na Elixir). Ale w Elixirze nie ma takiej konwencji, możesz z niej korzystać na własne ryzyko.

W Pythonie ten sam efekt można osiągnąć poprzez:

from math import * 

i zaleca się używać go tylko w niektórych specjalnych scenariuszach / trybie interaktywnym - do krótszego / szybszego pisania.

użyj i wymagaj

Co sprawia, use/ requireinna jest to, że odnoszą się one do „makro” - pojęcie, które nie istnieje w Python / Java / Golang ... rodziny.

Nie potrzebujesz importmodułu, aby korzystać z jego funkcji, ale potrzebujesz requiremodułu, aby używać jego makr :

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

Chociaż is_evenmożna go zapisać jako normalną funkcję, jest to makro, ponieważ:

W Elixirze Integer.is_odd / 1 jest zdefiniowane jako makro, dzięki czemu może być używane jako ochrona.

https://elixir-lang.org/getting-started/alias-require-and-import.html#require

use, fragment z dokumentu Elixir:

use wymaga danego modułu, a następnie wywołuje __using__/1wywołanie zwrotne, pozwalając modułowi na wstrzyknięcie kodu do bieżącego kontekstu.

defmodule Example do
  use Feature, option: :value
end

jest wkompilowany w

defmodule Example do
  require Feature
  Feature.__using__(option: :value)
end

https://elixir-lang.org/getting-started/alias-require-and-import.html#use

Więc pisanie use Xjest tym samym, co pisanie

require X
X.__using__()

use/2 jest makrem , makro przekształci kod na inny kod za Ciebie.

Będziesz chciał, use MODULEgdy:

  • chce uzyskać dostęp do jego makr ( require)
  • I wykonaj MODULE.__using__()

Testowane na Elixirze 1.5

HVNSweeting
źródło
3

use Module wymaga, Module a także wzywa __using__.

import Modulewprowadza Modulefunkcjonalność w aktualny kontekst , a nie tylko jej wymaga.

hagi-tragger
źródło
0

Import

Udostępnia wszystkie funkcje i makra z danego modułu w zakresie leksykalnym, w którym jest wywoływany. Należy pamiętać, że w większości przypadków do zaimportowania wystarczy jedna lub więcej funkcji / makr.

Przykład:

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

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

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

Posługiwać się

To makro umożliwia wstrzyknięcie dowolnego kodu w bieżącym module. Należy zachować ostrożność podczas korzystania z bibliotek zewnętrznych use, ponieważ możesz nie być pewien, co dokładnie dzieje się za kulisami.

Przykład:

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

Znajdujący się za sceną kod __using__został wstrzyknięty do TextPrintermodułu.

Nawiasem mówiąc, w Elixirze jest więcej instrukcji obsługi zależności .

szsoppa
źródło