【问题标题】:Elixir: use vs importElixir:使用与导入
【发布时间】:2015-04-14 00:07:48
【问题描述】:

useimport 有什么区别?

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

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

从其他模块导入函数和宏

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

还有其他区别吗?您什么时候会使用其中一个?

【问题讨论】:

  • 快速总结:import Module 引入了要在模块内部使用的函数。 use Module 引入要使用的函数并在您的模块上公开它们

标签: elixir


【解决方案1】:

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

【讨论】:

  • 所以说useimport 更通用准确吗?即import的功能是use的子集
  • import 仍然是必要的,因为我不知道说您可以单独使用use 重新实现import 是否准确,但如果可能的话,我不会感到惊讶。 use 绝对更强大。你可以用它做非常复杂的事情,例如,我在我的exprotobuf 项目中大量使用了use,如果你想看到它被推到极限,你可以查看它。您可以使用代码扩展模块、在编译时执行代码、向模块添加功能等。基本上它结合了import 和宏的强大功能。
  • 感谢代码的详细解释和参考。我想我现在明白了。我对 Elixir 还很陌生,但我认为一旦我查看更多用例,差异就会很明显。
  • 嘿,没问题,Phoenix Web 框架是另一个值得关注的地方。 Chris McCord 写了一本关于 Elixir 宏的书,他在 Phoenix 大量使用它们(包括 use)。对于初学者来说,它几乎肯定比exprotobuf 更容易阅读,但我想我可能会将use 推到exprotobuf 的极限,所以看看你能走多远可能很有用。
  • use实际上并没有做太多,它只是在指定的模块上调用__using__
【解决方案2】:

import ModuleModule 的所有函数和宏以 un-namespaced 方式带入您的模块中。

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 语句。

【讨论】:

  • 很好的答案!欲了解更多信息:elixir-lang.org/getting-started/alias-require-and-import.html
  • 进入 Elixir,来自 Python 世界,我对模块是 *.ex 文件和 defmodule 块以及如何从文件中提取模块感到有点困惑进入 iex REPL
  • 试图理解示例/概念。在这种特殊情况下,您只是在证明__using__ 方法是在use ModA 上执行的?在您提供的示例中,仅在 ModB 中使用 import 可能有意义吗?
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    参见 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一个模块会自动requires它。

    使用

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

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

    defmodule Example do
      use Feature, option: :value
    end
    

    编译成

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

    【讨论】:

      【解决方案5】:

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

      进口

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

      iex()> Integer.mod(5, 2)
      1
      
      iex()> String.trim(" Hello Elixir  ")
      "Hello Elixir"
      

      在 Python/Java/Golang 中,您需要 import MODULE 才能使用该 MODULE 中的函数,例如 Python

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

      那么,Elixir 中的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(影响 Elixir 的语言)中为 not recommended。但是 Elixir 中没有这样的约定,您可以自行承担使用它的风险。

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

      from math import * 
      

      并且它只推荐使用in some special scenarios / 交互模式 - 用于更短/更快的输入。

      使用和要求

      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 文档:

      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/2is a macro,宏会帮你把代码转换成其他代码。

      你会想use MODULE当你:

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

      在 Elixir 1.5 上测试

      【讨论】:

        【解决方案6】:

        导入

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

        例子:

        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.

        【讨论】:

          猜你喜欢
          • 2017-12-16
          • 2017-10-09
          • 1970-01-01
          • 2016-06-11
          • 1970-01-01
          • 2016-08-30
          • 2015-12-16
          • 2015-06-21
          • 2014-12-25
          相关资源
          最近更新 更多