elixir官方入門教程 別名,要求與進口

#別名,要求與進口編程

1. 別名
2. 要求
3. 進口
4. 使用
5. 理解別名
6. 模塊嵌套
7. 羣體別名/進口/要求/使用

爲了方便軟件複用,Elixir提供了三個命令(alias,requireimport)外加一個宏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

上述例子會定義兩個模塊:FooFoo.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不需先定義FooFoo.Bar).

下一章咱們將看到,別名在宏中扮演了關鍵角色,保證了它們的清潔性.

#羣體別名/進口/要求/使用

從Elixir v1.2開始,咱們能同時給多個對象賦別名,進口或要求模塊.當咱們開始嵌套模塊時,在構建Elixir應用時很經常使用,這會很是有用.例如,想象你有一個應用的全部模塊都嵌套在MyApp之下,你能夠同時爲MyApp.Foo,MyApp.BarMyApp.Baz賦予別名:

alias MyApp.{Foo, Bar, Baz}
相關文章
相關標籤/搜索