#别名,要求与进口编程
1. 别名 2. 要求 3. 进口 4. 使用 5. 理解别名 6. 模块嵌套 7. 群体别名/进口/要求/使用
为了方便软件复用,Elixir提供了三个命令(alias
,require
和import
)外加一个宏use
,简介以下:框架
# 给模块一个别名,使得咱们能够调用Bar来代替Foo.Bar alias Foo.Bar, as: Bar # 确保模块是编译好且可用的(经常使用于宏) require Foo # 进口Foo中的函数,使得能够不加前缀地调用它们 import Foo # 调用Foo中的代码定义做为扩展 use Foo
如今咱们将详细探索它们.记住前三条之因此被称为命令,是由于它们具备词法范围,而use
只是一个普通扩展点.async
#别名函数
alias
容许你为任何具名模块赋予别名.想象一下咱们的Math
模块要使用一个特殊的列表执行方法来作特定的数学操做:测试
defmodule Math do alias Math.List, as: List end
从如今起,任何提到List
的地方都会自动扩展成Math.List
.若是有人想访问原始的List
,就须要在以前加上模块名Elixir.
:ui
List.flatten #=> uses Math.List.flatten Elixir.List.flatten #=> uses List.flatten Elixir.Math.List.flatten #=> uses Math.List.flatten
全部在Elixir中定义的模块,都定义在一个主要Elixir命名空间中.为了方便,在调用它们时你能够省略"Elixir".atom
别名常常用于定义缩写.事实上,调用alias
时不带:as
,就会自动将模块名的最后部分设为别名.例如:code
alias Math.List
等同于对象
alias Math.List, as: List
注意alias
肯定了语法范围,能让你在特定的函数中设置别名:开发
defmodule Math do def plus(a, b) do alias Math.List # ... end def minus(a, b) do # ... end end
在上述例子中,因为咱们是在plus/2
函数中调用的alias
,因此别名只在函数plus/2
中可用.对minus/2
没有影响.
#要求
Elixir提供了宏做为元编程的机制(编写能生成代码的代码).
宏是在编译时执行和扩展的代码块.这意味着,为了使用一个宏,咱们要保证它的模块和实现都在编译过程当中可用.这经过require
命令完成:
iex> Integer.is_odd(3) ** (CompileError) iex:1: you must require Integer before invoking the macro Integer.is_odd/1 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
函数,咱们能够简单地进口它:
iex> import List, only: [duplicate: 2] List iex> duplicate :ok, 3 [:ok, :ok, :ok]
这时,咱们只进口了List
模块中的duplicate
函数(带两个参数).选项:only
的做用是避免将模块中的全部函数都导入命名空间中,选项:except
的做用是将模块中除了列表里的其它全部函数都导入.
import
也支持将:only
设置为:macros
或:functions
.例如,想要进口全部宏,能够这样写:
import Integer, only: :macros
或者进口全部函数:
import Integer, only: :functions
注意import
也有语法范围.这意味着咱们能够在函数定义中进口特定的宏或函数:
defmodule Math do def some_function do import List, only: [duplicate: 2] duplicate(:ok, 10) end end
在上述例子中,进口的List.duplicate/2
只在这个特定的函数中时可见的.duplicate/2
在这个模块中的其它任何函数中都是不可用的(或其它任何模块).
注意import
一个模块就自动require
了它.
#使用
虽然不是一个命令,但use
是一个与require
紧密关联的宏,能让你在当前内容中使用一个模块.开发者们常常用use
宏来往当前语法空间中添加外部功能,一般是模块.
例如,为了使用ExUnit框架来写测试,开发者须要使用ExUnit.Case
模块:
defmodule AssertionTest do use ExUnit.Case, async: true test "always pass" do assert true end end
在幕后,use
会要求给定的模块,而后在其中调用__using__/1
反馈,容许模块往当前内容注入一些代码.通常来讲,下面的模块:
defmodule Example do use Feature, option: :value end
被编译成
defmodule Example do require Feature Feature.__using__(option: :value) end
至此咱们关于Elixir模块的介绍几乎结束了.最后的话题是模块属性.
#理解别名
这时,你可能会想知道:究竟什么是Elixir中的别名,它是如何运做的?
Elixir中的别名是首字母大写的id(例如String
,Keyword
等等),在编译时会被转化成原子.举个例子,String
别名默认转化成原子:"Elixir.String"
:
iex> is_atom(String) true iex> to_string(String) "Elixir.String" iex> :"Elixir.String" == String true
使用alias/2
命令,咱们能够简单地修改别名要转化成的原子.
别名转化成原子是由于在Erlang虚拟机中模块老是用原子来表明.例如,这是咱们调用Erlang模块的机制:
iex> :lists.flatten([1, [2], 3]) [1, 2, 3]
这也是咱们之因此能动态地在一个模块中调用给定的函数:
iex> mod = :lists :lists iex> mod.flatten([1, [2], 3]) [1, 2, 3]
咱们简单地使用原子:list
调用了函数flatten
.
#模块嵌套
咱们已经讨论过了别名,如今咱们能够讨论嵌套以及它在Elixir中的运做方式.思考下面的例子:
defmodule Foo do defmodule Bar do end end
上述例子会定义两个模块:Foo
和Foo.Bar
.第二个能够被当作Bar
里的Foo
来访问,只要它们是在同一个语法空间里.上述代码等同于:
defmodule Elixir.Foo do defmodule Elixir.Foo.Bar do end alias Elixir.Foo.Bar, as: Bar end
若是以后Bar
模块在Foo
的模块定义以外被调用,那就必须使用它的全名(Foo.Bar
)或者别名.
注意:在Elixir中,你没必要再定义Foo.Bar
模块以前先定义Foo
模块,由于语言会将全部模块名转化为原子.你能够定义任意嵌套的模块而不须要定义任何链条上的模块(例如:Foo.Bar.Baz
不需先定义Foo
或Foo.Bar
).
下一章咱们将看到,别名在宏中扮演了关键角色,保证了它们的清洁性.
#群体别名/进口/要求/使用
从Elixir v1.2开始,咱们能同时给多个对象赋别名,进口或要求模块.当咱们开始嵌套模块时,在构建Elixir应用时很经常使用,这会很是有用.例如,想象你有一个应用的全部模块都嵌套在MyApp
之下,你能够同时为MyApp.Foo
,MyApp.Bar
和MyApp.Baz
赋予别名:
alias MyApp.{Foo, Bar, Baz}