Elixir:使用vs导入


134

use和之间有什么区别import

use是在当前上下文中使用给定模块的简单机制

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

从其他模块导入函数和宏

看起来有一个区别是import让您选择特定的功能/宏,同时use将所有内容都带入其中。

还有其他区别吗?您什么时候可以使用另一个?


快速总结: import Module引入要在模块内部使用的功能。 use Module引入要使用的函数并将其公开公开在您的模块上
Jered 18'Jan

Answers:


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



进入Elixir并从Python领域进入世界时,我对模块(即*.ex文件和defmodule块)以及如何将模块从文件中拉入iex REPL感到困惑
Nick T

2
试图了解示例/概念。在这种情况下,您只是在演示该__using__方法是在use ModA?上执行的。ModB在您提供的示例中正确使用import可能会有意义?
瑞安·尼尔·梅斯

35

use用于将代码注入当前模块,同时import又用于导入要使用的功能。use例如,您可以构建一个实现自动导入功能的实现,就像我在添加use Timex模块时使用Timex 一样,如果您想了解我的意思请查看timex.ex,这是一个如何构建a的非常简单的示例可以使用use的模块


1
那么说use比一般更准确import吗?也就是说,的功能importuse
User314159

1
import仍然是必要的,因为我不知道,如果它是准确的说,你可以重新实现importuse孤独,但如果可能的话,我不会感到惊讶。use虽然绝对更强大。您可以用它来做非常复杂的事情,例如,我use在我的exprotobuf项目中大量使用了它,您可以检查是否要查看它是否达到了极限。您可以使用代码扩展模块,在编译时执行代码,向模块添加功能等。基本上,它结合import了宏的功能。
bitwalker

感谢您的详细解释和对代码的引用。我想我现在明白了。我还是Elixir的新手,但我认为,一旦我查看更多用例,差异就会很明显。
User314159 2015年

没问题,Phoenix Web框架是另一个不错的选择。Chris McCord写了一本关于Elixir宏的书,他在Phoenix(包括use)中大量使用它们。对于初学者来说exprotobuf,阅读use本书几乎绝对比阅读起来容易,但是我想我可能exprotobuf会把它推到极限,因此,看看您能走多远,也许很有用。
bitwalker

5
use实际上并没有做什么,它只是调用__using__指定的模块。
Patrick Oscity

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(具有arity 2)List

请注意,import自动为模块添加模块require

尽管不是指令,但userequire它紧密相关的宏使您可以在当前上下文中使用模块。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的作用可能会让您感到惊讶:

每当我们想轻松访问其他模块中的函数或宏而无需使用完全限定的名称时,我们都会使用import

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

因此,如果您想输入sqrt而不是Integer.sqrttrim而不是String.trimimport将会有所帮助

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

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

这可能会导致阅读代码以及出现名称冲突时出现问题,因此不建议在Erlang(影响Elixir的语言)中使用。但是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 doc:

use需要给定的模块,然后__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__()

在Elixir 1.5上测试


3

use Module 需要 Module,也可以要求__using__它。

import ModuleModule功能带入当前环境中,而不仅仅是需要它。


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中有更多的依赖项处理指令

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.