13-alias,require和import

    下面我们将深入细节。记住前三个之所以称之为“指令”,
    是因为它们的作用域是词法作用域(lexicla scope)
    use是一个普通拓展点(common extension point),可以将宏展开。

    指令alias可以为任何模块设置别名。
    想象一下之前使用过的Math模块,它针对特殊的数学运算提供了特殊的列表(list)实现:

    1. defmodule Math do
    2. alias Math.List, as: List
    3. end

    现在,任何对List的引用将被自动变成对Math.List的引用。
    如果还想访问原来的List,可以加上它的模块名前缀’Elixir’:

    1. List.flatten #=> uses Math.List.flatten
    2. Elixir.List.flatten #=> uses List.flatten
    3. Elixir.Math.List.flatten #=> uses Math.List.flatten

    别名常被使用于定义快捷方式。实际应用中,不带:as选项调用alias
    自动将别名设置为该模块名称的最后一部分:

    1. alias Math.List

    就相当于:

    1. alias Math.List, as: List

    注意,alias词法作用域。也就是说,当你在某个函数中设置别名:

    1. defmodule Math do
    2. def plus(a, b) do
    3. alias Math.List
    4. # ...
    5. end
    6. # ...
    7. end
    8. end

    例子中alias指令设置的别名只在函数plus/2中有效,函数minus/2则不受影响。

    require

    Elixir提供了许多宏用于元编程(可以编写生成代码的代码)。

    宏是在编译时被执行和展开的代码。
    也就是说为了使用宏,你需要确保定义这个宏的模块及实现在你的代码的编译时可用(即被加载)。
    这使用require指令实现:

    Elixir中,Integer.odd?/1函数被定义为一个宏,因此它可以被当作卫兵表达式(guards)使用。
    为了调用这个宏,首先需要使用require引用Integer模块。

    总的来说,一个模块在被用到之前不需要早早地require,除非我们需要用到这个模块中定义的宏的时候。
    尝试调用一个没有加载的宏时,会报出一个异常。
    注意,像alias指令一样,require指令也是词法作用域的。
    在后面章节我们会进一步讨论宏。

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

    这个例子中,我们只从List模块导入了函数duplicate(元数是2的那个)。
    尽管:only选项是可选的,但是仍推荐使用,以避免向当前命名空间内导入这个模块内定义的所有函数。
    还有:except选项,可以排除一些函数而导入其余的。

    还有选项:only,传递给它:macros:functions,来导入该模块的所有宏或函数。
    如下面例子,程序仅导入Integer模块中定义的所有的宏:

    1. import Integer, only: :macros

    或者,仅导入所有的函数:

    1. import Integer, only: :functions

    注意,import也遵循词法作用域,意味着我们可以在某特定函数定义内导入宏或方法:

    1. defmodule Math do
    2. def some_function do
    3. import List, only: [duplicate: 2]
    4. duplicate(:ok, 10)
    5. end
    6. end

    在这个例子中,导入的函数List.duplicate/2只在函数some_function中可见,
    在该模块的其它函数中都不可用(自然,别的模块也不受影响)。

    注意,若import一个模块,将自动require它。

    use

    尽管不是一条指令,use是一个宏,与帮助你在当前上下文中使用模块的require指令联系紧密。
    use宏常被用来引入外部的功能到当前的词法作用域—-通常是模块。

    例如,在编写测试时,我们使用ExUnit框架。开发者需要使用ExUnit.Case 模块:

    1. defmodule AssertionTest do
    2. use ExUnit.Case, async: true
    3. test "always pass" do
    4. assert true
    5. end

    在代码背后,use宏先是require所给的模块,然后在模块上调用__using__/1回调函数,
    从而允许这个模块在当前上下文中注入某些代码。

    比如下面这个模块:

    会被编译成(即宏use扩展)

    1. defmodule Example do
    2. require Feature
    3. end

    到这里,关于Elixir的模块基本上讲得差不多了。后面会讲解模块的属性(Attribute)。

    Elixir中的别名是以大写字母开头的标识符(像String, Keyword),在编译时会被转换为原子。
    例如,别名‘String’默认情况下会被转换为原子:"Elixir.String"

    1. iex> is_atom(String)
    2. true
    3. iex> to_string(String)
    4. "Elixir.String"
    5. iex> :"Elixir.String" == String
    6. true

    使用alias/2指令,其实只是简单地改变了这个别名将要转换的结果。

    别名会被转换为原子,是因为在Erlang虚拟机(以及上面的Elixir)中,模块是由原子表述。
    例如,我们调用一个Erlang模块函数的机制是:

    1. iex> :lists.flatten([1,[2],3])
    2. [1, 2, 3]

    这也是允许我们动态调用模块函数的机制:

    1. iex> mod = :lists
    2. :lists
    3. iex> mod.flatten([1,[2],3])
    4. [1,2,3]

    我们只是简单地在原子:lists上调用了函数flatten

    模块嵌套

    我们已经介绍了别名,现在可以讲讲嵌套(nesting)以及它在Elixir中是如何工作的。
    考虑下面的例子:

    1. defmodule Foo do
    2. defmodule Bar do
    3. end
    4. end

    该例子定义了两个模块:FooFoo.Bar
    后者在Foo中可以用Bar为名来访问,因为它们在同一个词法作用域中。
    上面的代码等同于:

    如果之后开发者决定把Bar模块定义挪出Foo模块的定义,但是在Foo中仍然使用Bar来引用,
    那它就需要以全名(Foo.Bar)来命名,或者向上文提到的,在Foo中设置个别名来指代。

    注意: 在Elixir中,你并不是必须在定义Foo.Bar模块之前定义Foo模块,
    因为编程语言会将所有模块名翻译成原子。
    你可以定义任意嵌套的模块而不需要注意其名称链上的先后顺序
    (比如,在定义Foo.Bar.Baz前不需要提前定义foo或者Foo.Bar)。

    在后面几章我们会看到,别名在宏里面也扮演着重要角色,来保证它们是“干净”(hygienic)的。

    从Elixir v1.2版本开始,可以一次性使用alias,import,require操作多个模块。
    这在定义和使用嵌套模块的时候非常有用,这也是在构建Elixir程序的常见情形。

    例如,假设你的程序所有模块都嵌套在MyApp下,
    你可以一次同时给三个模块:MyApp.Foo,MyApp.BarMyApp.Baz提供别名: