长生不老药: 使用与进口

useimport有什么区别?

Use 是将给定模块用于当前上下文的简单机制

Https://hexdocs.pm/elixir/kernel

从其他模块导入函数和宏

看起来一个区别是 import让你选择特定的函数/宏,而 use带来了一切。

还有其他区别吗? 你什么时候会使用其中一个而不是另一个?

28605 次浏览

use用于向当前模块中注入代码,而 import用于导入函数以供使用。你可以构建一个自动导入函数的 use实现,例如,当你将 use Timex添加到模块 看看 Timex.ex 如果你想知道我的意思时,我对 Timex 所做的,这是一个如何构建一个可以是 use’d 的模块的非常简单的例子

import Module Module所有没有命名空间的函数和宏带入模块。

require Module 允许您使用 Module的宏,但不导入它们(Module的函数总是可用的命名空间)

use Module 第一个 requires模块,然后调用 Module上的 __using__宏。

考虑以下几点:

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时,它会生成一个插入到 ModB中的 import语句。

use Module 需要 Module,也在上面调用 __using__

import Module Module功能带入 当前背景,而不仅仅是需要它。

请参阅 “别名,要求和导入”页面的长生不老药官方入门指南:

# 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。例如,如果我们想多次使用 List模块中的 duplicate/2函数,我们可以导入它:

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

在这种情况下,我们只从 List导入函数 duplicate(带有第2工具)。

请注意,importing 模块会自动将其 require

使用

虽然不是指令,但 use是一个与 require紧密相关的宏,它允许您在当前上下文中使用模块。开发人员经常使用 use宏将外部功能引入当前的词法范围,通常是模块。

在幕后,use需要给定的模块,然后对其调用 __using__/1回调,允许模块将一些代码注入到当前上下文中。一般来说,以下单元:

defmodule Example do
use Feature, option: :value
end

被编译成

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

有着 Python/Java/Golang 语言背景的 importuse也让我感到困惑。这将通过一些声明性语言示例来解释代码重用机制。

进口

简而言之,在 Elixir 中,你不需要导入模块,所有的公共函数都可以通过完全限定的 MODULE.FuncION 语法来访问:

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


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

在 Python/Java/Golang 中,在使用该 MODULE 中的函数(例如 Python)之前,需要使用 import MODULE

In []: import math


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

那么,长生不老药中 import的作用可能会让你大吃一惊:

只要我们想方便地从其他模块访问函数或宏,而不使用完全限定名,就可以使用 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 的 不推荐(语言,影响长生不老药)。但是在长生不老药中没有这样的约定,你可以自担风险使用它。

在 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,摘自《长生不老》医生:

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__()

宏将为您将代码转换为其他代码。

当你:

  • 想要访问它的宏(require)
  • 执行 MODULE.__using__()

在长生不老药1.5上做过测试

进口

使得来自给定模块的所有函数和宏都可以在调用它的词法作用域内访问。请记住,在大多数情况下,您只需要导入一个或多个函数/宏。

例如:

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