Elixir: uso vs importação

134

Qual é a diferença entre usee import?

use é um mecanismo simples para usar um determinado módulo no contexto atual

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

Importa funções e macros de outros módulos

Parece que uma diferença é importescolher as funções / macros específicas, enquanto usetraz tudo.

Existem outras diferenças? Quando você usaria um sobre o outro?

Usuário314159
fonte
Resumo rápido: import Moduletraz funções para serem usadas dentro do seu módulo. use Moduletraz em funções a serem utilizados e os expõe publicamente seu módulo
Jered

Respostas:

213

import Moduletraz todas as funções e macros de Moduleun-namespaced para o seu módulo.

require Modulepermite que você use macros, Modulemas não as importe. (As funções de Modulesempre estão disponíveis no namespace.)

use Moduleprimeiro requiresmódulo e, em seguida, chama a __using__macro Module.

Considere o seguinte:

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

Isso não será compilado, pois ModA.moda()não foi importado ModB.

O seguinte será compilado:

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

Como quando você used ModAgerou uma importdeclaração de que foi inserido ModB.

Gregeg
fonte
6
Ótima resposta! Para obter mais informações: elixir-lang.org/getting-started/alias-require-and-import.html
justin
Obtendo-se para Elixir, e sendo do mundo Python Eu estou meio confuso sobre os módulos sendo *.exarquivos e os defmoduleblocos, e como você pode puxar um módulo a partir de um arquivo em um REPL IEX
Nick T
2
Tentando entender o exemplo / conceito. Nesse caso em particular, você está apenas demonstrando que o __using__método é executado use ModA? Provavelmente faria sentido usar apenas a importação no ModBexemplo que você apresentou correto?
Ryan-Neal Mes
35

usedestina-se a injetar código no módulo atual, enquanto importé usado para, bem, importar funções para uso. Você pode criar uma useimplementação que importe automaticamente funções, por exemplo, como eu faço com o Timex quando você adiciona use Timexa um módulo, dê uma olhada no timex.ex se quiser saber o que quero dizer , é um exemplo muito simples de como criar um módulo que pode ser use'd

bitwalker
fonte
1
Então, é preciso dizer que useé mais geral do que import? Ou seja, a funcionalidade de importé um subconjunto deuse
User314159 13/02/2015
1
importainda é necessário, como eu não sei se é correto dizer que você pode reimplementar importcom usesó, mas eu não ficaria surpreso se isso é possível. useé absolutamente mais poderoso. Você pode fazer coisas muito complexas com isso, por exemplo, uso muito useno meu exprotobufprojeto, que você pode conferir se quiser vê-lo ir ao limite. Você pode estender módulos com código, executar código em tempo de compilação, adicionar funções a um módulo, etc. Basicamente, ele combina importe o poder das macros.
bitwalker
obrigado pela explicação detalhada e referências ao código. Eu acho que entendi agora. Ainda sou novo no Elixir, mas acho que, depois de analisar mais casos de uso, as diferenças serão óbvias.
User314159
Ei, não há problema, outro ótimo lugar para procurar seria o quadro da web do Phoenix. Chris McCord escreveu um livro sobre macros Elixir e as usa muito em Phoenix (inclusive use). Definitivamente, é mais fácil ler para um iniciante do que exprotobuf, mas acho que provavelmente vou useao limite exprotobufpara que possa ser útil apenas para ver até onde você pode levá-lo.
21315 bitwalker
5
usena verdade, não faz muito, apenas chama __using__o módulo especificado.
Patrick Oscity
25

Consulte a página «alias, exija e importe» no guia de introdução oficial do 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

Exigir

O Elixir fornece macros como um mecanismo para metaprogramação (escrever código que gera código).

Macros são pedaços de código que são executados e expandidos no momento da compilação. Isso significa que, para usar uma macro, precisamos garantir que seu módulo e implementação estejam disponíveis durante a compilação. Isso é feito com a requirediretiva.

Em geral, um módulo não precisa ser necessário antes do uso, exceto se queremos usar as macros disponíveis nesse módulo.

Importar

Usamos importsempre que queremos acessar funções ou macros facilmente de outros módulos sem usar o nome completo. Por exemplo, se queremos usar a duplicate/2função do Listmódulo várias vezes, podemos importá-la:

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

Nesse caso, estamos importando apenas a função duplicate(com arity 2) de List.

Observe que importum módulo o executa automaticamente require.

Usar

Embora não seja uma diretiva, useé uma macro fortemente relacionada a requireisso que permite usar um módulo no contexto atual. A usemacro é freqüentemente usada pelos desenvolvedores para trazer funcionalidade externa para o escopo lexical atual, geralmente módulos.

Nos bastidores, userequer o módulo fornecido e, em seguida, chama o __using__/1retorno de chamada, permitindo que o módulo injete algum código no contexto atual. De um modo geral, o seguinte módulo:

defmodule Example do
  use Feature, option: :value
end

é compilado em

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

Com experiência nas linguagens Python / Java / Golang, o importvs usetambém foi confuso para mim. Isso explicará o mecanismo de reutilização de código com alguns exemplos de linguagens declarativas.

importar

Em resumo, no Elixir, você não precisa importar módulos. Todas as funções públicas podem ser acessadas pela sintaxe MODULE.FUNCTION totalmente qualificada:

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

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

Em Python / Java / Golang, você precisa import MODULEantes de poder usar funções nesse MODULE, por exemplo, Python

In []: import math

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

Então, o que o importElixir faz pode surpreendê-lo:

Utilizamos a importação sempre que queremos acessar facilmente funções ou macros de outros módulos sem usar o nome completo.

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

Então, se você quiser digitar em sqrtvez de Integer.sqrt, em trimvez de String.trim, importajudará

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

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

Isso pode causar problemas na leitura do código e, quando houver conflito de nome, não é recomendado no Erlang (o idioma que influencia o Elixir). Mas não existe essa convenção no Elixir, você pode usá-lo por sua conta e risco.

No Python, o mesmo efeito pode ser feito por:

from math import * 

e recomenda-se usar apenas em alguns cenários especiais / modo interativo - para digitação mais curta / mais rápida.

use e exija

O que faz use/ requirediferente é que eles se relacionam com "macro" - o conceito que não existe na família Python / Java / Golang ....

Você não precisa de importum módulo para usar suas funções, mas precisa de requireum módulo para usar suas macros :

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

Embora is_evenpossa ser escrita como uma função normal, é uma macro porque:

No Elixir, Integer.is_odd / 1 é definido como uma macro para que possa ser usado como uma proteção.

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

use, para extrair do Elixir doc:

use requer o módulo fornecido e, em seguida, chama o __using__/1retorno de chamada, permitindo que o módulo injete algum código no contexto atual.

defmodule Example do
  use Feature, option: :value
end

é compilado em

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

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

Então escrever use Xé o mesmo que escrever

require X
X.__using__()

use/2 é uma macro , a macro transformará o código em outro código para você.

Você vai querer use MODULEquando você:

  • quer acessar suas macros ( require)
  • E executar MODULE.__using__()

Testado em Elixir 1.5

HVNSweeting
fonte
3

use Module requer Module e também solicita __using__.

import Moduletraz Modulefuncionalidade ao contexto atual , não apenas exige isso.

hagi-tragger
fonte
0

Importar

Torna todas as funções e macros de um determinado módulo acessíveis dentro do escopo lexical onde é chamado. Lembre-se de que, na maioria dos casos, é necessário importar apenas uma ou mais funções / macros.

Exemplo:

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

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

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

Usar

Essa macro permite injetar qualquer código no módulo atual. Você deve ter cuidado ao usar bibliotecas externas use, pois pode não ter certeza do que exatamente acontece nos bastidores.

Exemplo:

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

Nos bastidores, o código da cena __using__foi injetado no TextPrintermódulo.

A propósito, há mais instruções de manipulação de dependência no Elixir .

szsoppa
fonte