Elixir学习笔记——别名、需要、导入和使用

为了便于软件重用,Elixir 提供了三个指令(alias、require 和 import)以及一个名为 use 的宏,总结如下:

# 为模块添加别名,以便可以将其称为 Bar 而不是 Foo.Bar
alias Foo.Bar, as: Bar

# 需要模块才能使用其宏
require Foo

# 从 Foo 导入函数,以便可以在不带 `Foo.` 前缀的情况下调用它们
import Foo

# 调用 Foo 中定义的自定义代码作为扩展点
use Foo

我们现在将详细探讨它们。请记住,前三个被称为指令,因为它们具有词法范围,而 use 是一个允许使用的模块注入代码的通用扩展点。

别名

alias 允许您为任何给定的模块名称设置别名。

假设一个模块使用 Math.List 中实现的专用列表。alias 指令允许在模块定义中将 Math.List 引用为 List:

原始 List 仍可通过完全限定名称 Elixir.List 在 Stats 中访问。

Elixir 中定义的所有模块都在主 Elixir 命名空间内定义,例如 Elixir.String。但是,为了方便起见,您可以在引用它们时省略“Elixir。”

别名经常用于定义快捷方式。事实上,调用不带 :as 选项的 alias 会自动将别名设置为模块名称的最后一部分,例如:

与以下相同:

请注意,alias 是词法作用域,这允许您在特定函数内设置别名:

在上面的例子中,由于我们在函数 plus/2 内调用 alias,因此别名仅在函数 plus/2 内有效。minus/2 不会受到影响。

需要

Elixir 提供宏作为元编程(编写代码生成代码)的机制。宏在编译时展开。

模块中的公共函数是全局可用的,但为了使用宏,您需要通过要求定义它们的模块来选择加入。

iex>  Integer.is_odd(3)
** (UndefinedFunctionError) function Integer.is_odd/1 is undefined or private. However, there is a macro with the same name and arity. Be sure to require Integer if you intend to invoke this macro
    (elixir) Integer.is_odd(3)
iex>  require Integer
Integer
iex>  Integer.is_odd(3)
true

在 Elixir 中,Integer.is_odd/1 被定义为宏,以便可以将其用作保护。这意味着,为了调用 Integer.is_odd/1,我们需要首先要求 Integer 模块。

请注意,与 alias 指令一样,require 也是词法作用域。我们将在后面的章节中详细讨论宏。

导入

每当我们想从其他模块访问函数或宏而不使用完全限定名称时,我们都会使用 import。请注意,我们只能导入公共函数,因为私有函数永远无法从外部访问。

例如,如果我们想多次使用 List 模块中的 duplicate/2 函数,我们可以导入它:

我们只从 List 导入了函数 duplicate(参数为 2)。虽然 :only 是可选的,但建议使用它,以避免在当前范围内导入给定模块的所有函数。:except 也可以作为选项提供,以便导入模块中除函数列表之外的所有内容。

请注意,import 也是词法范围的。这意味着我们可以在函数定义中导入特定的宏或函数:

在上面的例子中,导入的 List.duplicate/2 仅在该特定函数中可见。duplicate/2 在该模块(或任何其他模块)的任何其他函数中都不可用。

虽然导入对于框架和库构建抽象很有用,但开发人员通常应该更喜欢在自己的代码库中使用别名进行导入,因为别名使被调用函数的来源更清晰。

使用

use 宏经常用作扩展点。这意味着,当您使用模块 FooBar 时,您允许该模块在当前模块中注入任何代码,例如导入自身或其他模块、定义新函数、设置模块状态等。

例如,为了使用 ExUnit 框架编写测试,开发人员应该使用 ExUnit.Case 模块:

在后台,use 需要给定的模块,然后对其调用 __using__/1 回调,允许模块将一些代码注入当前上下文。一些模块(例如,上面的 ExUnit.Case,还有 Supervisor 和 GenServer)使用此机制为您的模块填充一些基本行为,您的模块旨在覆盖或完成这些行为。

一般来说,以下模块:

编译为

由于 use 允许运行任何代码,因此如果不阅读其文档,我们无法真正了解使用模块的副作用。因此,请谨慎使用此功能,并且仅在严格需要时才使用。不要在可以使用导入或别名的地方使用 use。

理解别名

此时,您可能想知道:Elixir 别名到底是什么,它是如何表示的?

Elixir 中的别名是一个大写的标识符(如字符串、关键字等),在编译期间会转换为原子。例如,String 别名默认转换为原子 :"Elixir.String":

通过使用 alias/2 指令,我们可以改变别名扩展为的原子。

别名扩展为原子,因为在 Erlang 虚拟机(以及 Elixir)中,模块始终由原子表示:

这是我们用来调用 Erlang 模块的机制:

模块嵌套

既然我们已经讨论了别名,我们可以讨论嵌套以及它在 Elixir 中的工作原理。请考虑以下示例:

上面的示例将定义两个模块:Foo 和 Foo.Bar。只要它们在同一个词法范围内,第二个模块就可以在 Foo 内作为 Bar 访问。

如果稍后将 Bar 模块移出 Foo 模块定义,则必须通过其全名 (Foo.Bar) 引用它,或者必须使用上面讨论的别名指令设置别名。

注意:在 Elixir 中,您不必在定义 Foo.Bar 模块之前定义 Foo 模块,因为它们实际上是独立的。上面的代码也可以写成:

为嵌套模块添加别名不会将父模块纳入范围。请考虑以下示例:

正如我们将在后面的章节中看到的那样,别名在宏中也起着至关重要的作用,以确保它们是健壮的结构。

多别名/导入/需要/使用

可以同时为多个模块设置别名、导入、要求或使用多个模块。当我们开始嵌套模块时,这尤其有用,这在构建 Elixir 应用程序时非常常见。例如,假设您有一个应用程序,其中所有模块都嵌套在 MyApp 下,您可以同时为模块 MyApp.Foo、MyApp.Bar 和 MyApp.Baz 设置别名,如下所示:

至此,我们完成了对 Elixir 模块的介绍。下一个要介绍的主题是模块属性。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值