Elixir: uso vs importación

134

¿Cuál es la diferencia entre usey import?

El uso es un mecanismo simple para usar un módulo dado en el contexto actual

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

Importa funciones y macros de otros módulos

Parece que una diferencia es importque le permite elegir las funciones / macros específicas, mientras que usetrae todo adentro.

¿Hay otras diferencias? ¿Cuándo usarías uno sobre el otro?

Usuario314159
fuente
Resumen rápido: import Moduletrae funciones para ser utilizadas dentro de su módulo. use Moduletrae funciones para ser utilizadas Y las expone públicamente en su módulo
Jered

Respuestas:

213

import Moduletrae todas las funciones y macros de Moduleespacios sin nombre a su módulo.

require Modulele permite usar macros de Modulepero no las importa. (Las funciones de Modulesiempre están disponibles en espacios de nombres).

use Moduleprimer requiresmódulo y luego llama a la __using__macro Module.

Considera lo siguiente:

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

Esto no se compilará ya que ModA.moda()no se ha importado ModB.

Sin embargo, se compilará lo siguiente:

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

Al igual que cuando useD ModAse genera un importcomunicado que se insertó en ModB.

Gregreg
fuente
66
¡Gran respuesta! Para más información: elixir-lang.org/getting-started/alias-require-and-import.html
justin el
Al ingresar a Elixir y ser del mundo de Python, estoy un poco confundido acerca de que los módulos sean *.exarchivos y defmodulebloques, y cómo extraería un módulo de un archivo a un iex REPL
Nick T
2
Tratando de entender el ejemplo / concepto. En este caso particular, ¿solo está demostrando que el __using__método se ejecuta en use ModA? Probablemente tendría sentido usar import ModBen el ejemplo que ha presentado, ¿correcto?
Ryan-Neal Mes
35

useestá destinado a inyectar código en el módulo actual, mientras que importse usa, bueno, para importar funciones para su uso. Puede construir una useimplementación que importe automáticamente funciones, por ejemplo, como hago con Timex cuando agrega use Timexa un módulo, eche un vistazo a timex.ex si desea saber a qué me refiero , es un ejemplo muy simple de cómo construir un módulo que puede ser use'd

Bitwalker
fuente
1
Entonces, ¿es exacto decir que usees más general que import? Es decir, la funcionalidad de importes un subconjunto deuse
User314159
1
importsigue siendo necesario, ya que no sé si es exacto decir que se puede volver importa implementar usesolo, pero no me sorprendería si fuera posible. usees absolutamente más poderoso sin embargo. Puede hacer cosas muy complejas con él, por ejemplo, hago un uso intensivo de usemi exprotobufproyecto que puede consultar si desea verlo llevado al límite. Puede extender módulos con código, ejecutar código en tiempo de compilación, agregar funciones a un módulo, etc. Básicamente combina importy el poder de las macros.
bitwalker
Gracias por la explicación detallada y las referencias al código. Creo que lo entiendo ahora. Todavía soy nuevo en Elixir, pero creo que una vez que vea más casos de uso, las diferencias serán obvias.
Usuario314159
Hola, no hay problema, otro gran lugar para buscar sería el marco web de Phoenix. Chris McCord ha escrito un libro sobre macros de Elixir, y los usa mucho en Phoenix (incluido use). Definitivamente será más fácil leerlo para un principiante que exprotobuf, pero creo que probablemente llego usea su límite, por exprotobuflo que podría ser útil solo para ver hasta dónde puede llegar.
bitwalker
55
useen realidad no hace mucho, solo llama __using__al módulo especificado.
Patrick Oscity
25

Consulte la página «alias, requerir e importar» de la guía de inicio oficial de 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

Elixir proporciona macros como mecanismo para la metaprogramación (escribir código que genera código).

Las macros son fragmentos de código que se ejecutan y expanden en el momento de la compilación. Esto significa que, para usar una macro, debemos garantizar que su módulo y su implementación estén disponibles durante la compilación. Esto se hace con la requiredirectiva.

En general, no es necesario un módulo antes de su uso, excepto si queremos usar las macros disponibles en ese módulo.

Importar

Usamos importcuando queremos acceder fácilmente a funciones o macros desde otros módulos sin usar el nombre completo. Por ejemplo, si queremos usar la duplicate/2función del Listmódulo varias veces, podemos importarla:

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

En este caso, estamos importando solo la función duplicate(con arity 2) de List.

Tenga en cuenta que importun módulo lo envía automáticamente require.

Utilizar

Aunque no es una directiva, usees una macro estrechamente relacionada requireque le permite utilizar un módulo en el contexto actual. Los usedesarrolladores utilizan con frecuencia la macro para llevar la funcionalidad externa al ámbito léxico actual, a menudo módulos.

Detrás de escena, userequiere el módulo dado y luego llama a la __using__/1devolución de llamada, lo que permite que el módulo inyecte algo de código en el contexto actual. En general, el siguiente módulo:

defmodule Example do
  use Feature, option: :value
end

se compila en

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

Con antecedentes de los lenguajes Python / Java / Golang, el importvs usetambién estaba confundido para mí. Esto explicará el mecanismo de reutilización de código con algunos ejemplos de lenguajes declarativos.

importar

En resumen, en Elixir, no necesita importar módulos. Se puede acceder a todas las funciones públicas mediante la sintaxis MODULE.FUNCTION completa:

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

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

En Python / Java / Golang, debe hacerlo import MODULEantes de poder usar funciones en ese MÓDULO, por ejemplo, Python

In []: import math

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

Entonces, lo que hace importen Elixir puede sorprenderte:

Usamos importar cuando queremos acceder fácilmente a funciones o macros desde otros módulos sin usar el nombre completo

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

Entonces, si desea escribir en sqrtlugar de Integer.sqrt, en trimlugar de String.trim, importayudará

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

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

Esto puede causar problemas para leer el código y cuando hay un conflicto de nombres, por lo que no se recomienda en Erlang (el idioma que influye en Elixir). Pero no existe tal convención en Elixir, puede usarla bajo su propio riesgo.

En Python, el mismo efecto se puede hacer al:

from math import * 

y solo se recomienda usar en algunos escenarios especiales / modo interactivo, para una escritura más corta / más rápida.

usar y requerir

Lo que hace use/ requirediferente es que se relacionan con "macro", el concepto que no existe en Python / Java / Golang ... family.

No necesita importun módulo para usar sus funciones, pero necesita requireun módulo para usar sus 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

Aunque is_evenpuede escribirse como una función normal, es una macro porque:

En Elixir, Integer.is_odd / 1 se define como una macro para que pueda usarse como guardia.

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

use, para extraer de Elixir doc:

el uso requiere el módulo dado y luego llama a la __using__/1devolución de llamada en él permitiendo que el módulo inyecte algún código en el contexto actual.

defmodule Example do
  use Feature, option: :value
end

se compila en

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

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

Entonces escribir use Xes lo mismo que escribir

require X
X.__using__()

use/2 es una macro , la macro transformará el código en otro código para usted.

Querrás use MODULEcuando:

  • desea acceder a sus macros ( require)
  • Y ejecutar MODULE.__using__()

Probado en Elixir 1.5

HVNSweeting
fuente
3

use Module requiere Module y también lo llama __using__.

import Moduletrae la Modulefuncionalidad al contexto actual , no solo la requiere.

hagi-tragger
fuente
0

Importar

Hace que todas las funciones y macros de un módulo dado sean accesibles dentro del ámbito léxico donde se llama. Tenga en cuenta que en la mayoría de los casos solo necesita importar una o más funciones / macros.

Ejemplo:

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

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

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

Utilizar

Esta macro le permite inyectar cualquier código en el módulo actual. Debe tener cuidado al usar bibliotecas externas con use, ya que es posible que no esté seguro de lo que sucede exactamente detrás de escena.

Ejemplo:

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

Detrás de la escena, el código dentro de __using__ha sido inyectado en el TextPrintermódulo.

Por cierto, hay más instrucciones de manejo de dependencias en Elixir .

szsoppa
fuente