Elixir:使用与导入

Elixir: use vs import

useimport 有什么区别?

use is a simple mechanism for using a given module into the current context

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

Imports function and macros from other modules

看起来有一个区别是 import 让您选择特定的 functions/macros 而 use 将所有内容都包含在内。

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

use 用于将代码注入当前模块,而 import 用于导入函数以供使用。您可以构建一个自动导入函数的 use 实现,例如,当您将 use Timex 添加到模块 take a look at timex.ex if you want to know what I mean 时,我对 Timex 所做的,这是一个如何构建一个非常简单的示例可以 use'd

的模块

import ModuleModule 的所有函数和宏都以未命名空间的形式引入您的模块。

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 时,它生成了一个 import 语句,该语句被插入 ModB.

use Module 需要 Module 并在其上调用 __using__

import ModuleModule 功能引入 当前上下文 ,而不仅仅是需要它。

请参阅 elixir 官方入门指南中的 «alias, require, and import» 页:

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

请注意 import 会自动 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 中,您不需要导入模块。所有 public 函数都可以通过完全限定的 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

那么 Elixir 中 import 的功能可能会让您大吃一惊:

We use import whenever we want to easily access functions or macros from other modules without using the fully-qualified name

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 的语言)中是 not recommended。但是 Elixir 中没有这样的约定,您可以自行承担风险。

在Python中,同样的效果可以通过:

from math import * 

它只推荐使用 in some special scenarios / 交互模式 - 用于 shorter/faster 打字。

使用和要求

use/require 的不同之处在于它们与 "macro" 相关 - 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可以写成普通函数,但它是一个宏,因为:

In Elixir, Integer.is_odd/1 is defined as a macro so that it can be used as a guard.

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

use,摘自 Elixir 文档:

use requires the given module and then calls the __using__/1 callback on it allowing the module to inject some code into the current context.

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 is a macro,宏会为您将代码转换成其他代码。

你会想要 use MODULE 当你:

  • 想要访问其宏 (require)
  • 并执行MODULE.__using__()

在 Elixir 1.5 上测试

导入

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

示例:

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 模块中。

By the way, there is more dependency handling instructions in Elixir.