#別名,要求與進口編程
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}