Elixir: utiliser vs importer

135

Quelle est la différence entre useet import?

use est un mécanisme simple pour utiliser un module donné dans le contexte actuel

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

Importe la fonction et les macros d'autres modules

Il semble qu'une différence soit importque vous sélectionniez les fonctions / macros spécifiques usetout en apportant tout.

Y a-t-il d'autres différences? Quand utiliseriez-vous l'un sur l'autre?

Utilisateur314159
la source
Résumé rapide: import Moduleapporte des fonctions à utiliser dans votre module. use Moduleapporte des fonctions à utiliser ET les expose publiquement sur votre module
Jered

Réponses:

213

import Moduleapporte toutes les fonctions et macros de Moduleun-namespaced dans votre module.

require Modulevous permet d'utiliser des macros Modulemais ne les importe pas. (Les fonctions de Modulesont toujours disponibles avec un espace de noms.)

use Modulepremier requiresmodule, puis appelle la __using__macro Module.

Considérer ce qui suit:

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

Cela ne sera pas compilé car il ModA.moda()n'a pas été importé dans ModB.

Ce qui suit va cependant compiler:

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

Comme lorsque vous useas- ModAil produit une importdéclaration qui a été inséré dans ModB.

Greggreg
la source
6
Très bonne réponse! Pour plus d'informations: elixir-lang.org/getting-started/alias-require-and-import.html
justin
Entrer dans Elixir, et étant du monde Python, je suis un peu confus au sujet des modules étant des *.exfichiers et des defmoduleblocs, et comment vous tireriez un module d'un fichier dans un iex REPL
Nick T
2
Essayer de comprendre l'exemple / le concept. Dans ce cas particulier, vous démontrez simplement que la __using__méthode est exécutée sur use ModA? Il serait probablement judicieux d'utiliser simplement l'importation ModBdans l'exemple que vous avez présenté, correct?
Ryan-Neal Mes
35

useest destiné à injecter du code dans le module actuel, alors qu'il importest utilisé pour importer des fonctions à utiliser. Vous pouvez créer une useimplémentation qui importe automatiquement des fonctions par exemple, comme je le fais avec Timex lorsque vous ajoutez use Timexà un module, jetez un œil à timex.ex si vous voulez savoir ce que je veux dire , c'est un exemple très simple de comment construire un module qui peut être use'd

Bitwalker
la source
1
Alors, est-il exact de dire que usec'est plus général que import? Autrement dit, la fonctionnalité de importest un sous-ensemble deuse
User314159
1
importest encore nécessaire, car je ne sais pas s'il est exact de dire que vous pouvez ré - écrire importavec useseul, mais je ne serais pas surpris si cela est possible. useest absolument plus puissant cependant. Vous pouvez faire des choses très complexes avec, par exemple, je l'utilise beaucoup usedans mon exprotobufprojet que vous pouvez vérifier si vous voulez le voir poussé à ses limites. Vous pouvez étendre des modules avec du code, exécuter du code à la compilation, ajouter des fonctions à un module, etc. Fondamentalement, il combine importet la puissance des macros.
bitwalker
merci pour l'explication détaillée et les références au code. Je pense que je comprends maintenant. Je suis encore nouveau sur Elixir, mais je pense qu'une fois que je regarderai plus de cas d'utilisation, les différences seront évidentes.
User314159
Hé pas de problème, un autre bon endroit à regarder serait le framework Web Phoenix. Chris McCord a écrit un livre sur les macros Elixir, et il les utilise beaucoup à Phoenix (y compris use). C'est presque certainement plus facile à lire pour un débutant que exprotobuf, mais je pense que je pousse probablement useà sa limite, exprotobufdonc il pourrait être utile de voir jusqu'où vous pouvez aller.
bitwalker
5
useen fait ne fait pas grand-chose, il appelle simplement __using__le module spécifié.
Patrick Oscity
25

Voir la page «alias, require et import» du guide de démarrage officiel 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

Exiger

Elixir fournit des macros comme mécanisme de méta-programmation (écriture de code qui génère du code).

Les macros sont des morceaux de code qui sont exécutés et développés au moment de la compilation. Cela signifie que pour utiliser une macro, nous devons garantir que son module et son implémentation sont disponibles lors de la compilation. Cela se fait avec la requiredirective.

En général, un module n'a pas besoin d'être requis avant l'utilisation, sauf si nous voulons utiliser les macros disponibles dans ce module.

Importer

Nous utilisons importchaque fois que nous voulons accéder facilement aux fonctions ou aux macros à partir d'autres modules sans utiliser le nom complet. Par exemple, si nous voulons utiliser la duplicate/2fonction du Listmodule plusieurs fois, nous pouvons l'importer:

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

Dans ce cas, nous importons uniquement la fonction duplicate(avec l'arité 2) de List.

Notez que importle module est automatiquement intégré require.

Utilisation

Bien que n'étant pas une directive, useest une macro étroitement liée à requirequi vous permet d'utiliser un module dans le contexte actuel. La usemacro est fréquemment utilisée par les développeurs pour intégrer des fonctionnalités externes dans la portée lexicale actuelle, souvent des modules.

Dans les coulisses, usenécessite le module donné, puis appelle le __using__/1rappel sur celui-ci permettant au module d'injecter du code dans le contexte actuel. De manière générale, le module suivant:

defmodule Example do
  use Feature, option: :value
end

est compilé dans

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

Avec l'expérience des langages Python / Java / Golang, le importvs useétait également confus pour moi. Cela expliquera le mécanisme de réutilisation du code avec quelques exemples de langages déclaratifs.

importer

En bref, dans Elixir, vous n'avez pas besoin d'importer de modules. Toutes les fonctions publiques sont accessibles par la syntaxe complète MODULE.FUNCTION:

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

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

En Python / Java / Golang, vous devez import MODULEavant de pouvoir utiliser des fonctions dans ce MODULE, par exemple Python

In []: import math

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

Alors ce que fait importElixir pourrait vous surprendre:

Nous utilisons l'importation chaque fois que nous voulons accéder facilement aux fonctions ou aux macros d'autres modules sans utiliser le nom complet

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

Donc, si vous voulez taper sqrtau lieu de Integer.sqrt, trimau lieu de String.trim, importcela vous aidera

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

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

Cela peut poser des problèmes pour la lecture du code et en cas de conflit de nom, ce n'est donc pas recommandé dans Erlang (le langage qui influence Elixir). Mais il n'y a pas de telle convention dans Elixir, vous pouvez l'utiliser à vos risques et périls.

En Python, le même effet peut être fait par:

from math import * 

et il n'est recommandé de l'utiliser que dans certains scénarios spéciaux / mode interactif - pour une saisie plus courte / plus rapide.

utiliser et exiger

Ce qui rend use/ requiredifférent, c'est qu'ils se rapportent à "macro" - le concept qui n'existe pas dans la famille Python / Java / Golang ....

Vous n'avez pas besoin d' importun module pour utiliser ses fonctions, mais vous avez besoin d' requireun module pour utiliser ses 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

Bien que cela is_evenpuisse être écrit comme une fonction normale, il s'agit d'une macro car:

Dans Elixir, Integer.is_odd / 1 est défini comme une macro afin de pouvoir être utilisé comme garde.

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

use, extrait de Elixir doc:

use requiert le module donné et appelle ensuite le __using__/1rappel sur celui-ci permettant au module d'injecter du code dans le contexte actuel.

defmodule Example do
  use Feature, option: :value
end

est compilé dans

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

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

Donc l'écriture use Xest la même chose que l'écriture

require X
X.__using__()

use/2 est une macro , la macro transformera le code en un autre code pour vous.

Vous voudrez use MODULEquand vous:

  • voulez accéder à ses macros ( require)
  • ET exécuter MODULE.__using__()

Testé sur Elixir 1.5

HVNSweeting
la source
3

use Module exige Module et fait également appel __using__à lui.

import Moduleapporte la Modulefonctionnalité dans le contexte actuel , pas seulement l'exige.

hagi-tragger
la source
0

Importer

Rend toutes les fonctions et macros d'un module donné accessibles à l'intérieur de la portée lexicale où il est appelé. Gardez à l'esprit que dans la plupart des cas, vous n'avez besoin que d'une ou plusieurs fonctions / macros à importer.

Exemple:

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

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

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

Utilisation

Cette macro vous permet d'injecter n'importe quel code dans le module actuel. Vous devez être prudent lorsque vous utilisez des bibliothèques externes avec use, car vous ne savez peut-être pas ce qui se passe exactement dans les coulisses.

Exemple:

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

Derrière la scène, le code à l'intérieur de __using__a été injecté dans le TextPrintermodule.

À propos, il y a plus d'instructions de gestion des dépendances dans Elixir .

szsoppa
la source