use Module
除了在Module
上調用__using__
宏外, 不作其餘任何事情. import Module
會把Module
模塊的全部非私有函數和宏引入到當前模塊中, 能夠直接經過名字調用函數和宏. require Module
容許使用一個模塊的宏, 但並不導入他們. 必須經過全名(帶名稱空間)來引用.shell
例如:函數
defmodule User do defmacro __using__(_opts) do IO.puts "use User" end def get_username() do "developerworks" end end
defmodule Hello do use User def hi() do IO.puts "Hello, #{get_username()}" # User模塊未被導入,該函數不存在 end end
再來看下面的例子:測試
defmodule User do defmacro __using__(_opts) do quote do import User end end def get_username() do "developerworks" end end defmodule ModB do use User def hi() do IO.puts "Hello, #{get_username()}" # User模塊已導入, 該函數存在 end end
當你使用use User
的時候, 它生產了一個import
語句, 把User
的函數和宏插入到Hello
中.ui
下面是一個實際的例子, 一個Zlib模塊用於壓縮和解壓數據, 代碼能夠保存爲zlib.exs
腳本文件方便測試code
defmodule Zlib do @moduledoc """ Zlib compress and decompress functions """ @doc """ use Zlib """ defmacro __using__(_opts) do quote do import Zlib end end def compress(data) do z = :zlib.open() :zlib.deflateInit(z) :zlib.deflate(z, data) result = :zlib.deflate(z, << >>, :finish) :zlib.deflateEnd(z) :erlang.list_to_binary(result) end def decompress(data, wbits \\ 15) do z = :zlib.open() :zlib.inflateInit(z, wbits) [result|_] = :zlib.inflate(z, data) :zlib.inflateEnd(z) result end end require Logger use Zlib compressed_data = Zlib.compress("test") Logger.info "#{inspect compressed_data}" decompressed_data = Zlib.decompress(compressed_data) Logger.info "#{decompressed_data}"
上面的代碼實際上運行的時候回出錯, 經過個人測試use Zlib
只能用在模塊內部. 不能直接在全局空間中使用, 正確的調用代碼以下:作用域
defmodule Test do @moduledoc """ 壓縮和解壓縮測試模塊. """ def run() do require Logger # 把Zlib模塊中的函數和宏導入到當前模塊的做用域中. use Zlib compressed_data = compress("test") Logger.info "#{inspect compressed_data}" decompressed_data = decompress(compressed_data) Logger.info "#{decompressed_data}" end end Test.run
經過上述實驗, 咱們在模塊Zlib中定義了一個 __using__
宏, 正如文章最開始所述, use Zlib
惟一的做用就是調用目標模塊Zlib
中的__using__
宏, 在__using__
宏中咱們import
了自身, 也就是導入了Zlib
模塊中的全部公共(定義爲def
而非defp
)的函數和宏.get
能夠實驗一下, 若是你在Zlib
模塊中增長一個私有函數(private_test
), 那麼咱們use Zlib
後在模塊外部調用private_test
是會發生以下錯誤的.it
zlib.exs:33: warning: function private_test/0 is unused ** (CompileError) zlib.exs:53: undefined function private_test/0 (stdlib) lists.erl:1337: :lists.foreach/2 zlib.exs:41: (file) (elixir) lib/code.ex:363: Code.require_file/2
另外對於import
, 還有only
, except
選項, 分別表示只導入指定的函數, 以及排除指定的函數, 例如:io
import Zlib, only: compress import Zlib, except: decompress
有的模塊中包含多個同名, 參數數(arity)不一樣的函數, 你甚至能夠按函數的參數個數來指定要導入的特定函數, 好比, 在Elixir內置的模塊List
中存在List.to_integer/1
和List.to_integer/2
兩個名爲to_integer
的函數, 若是你只想導入to_integer/1
,那麼能夠按下面的方式來導入:編譯
import List, only: [to_integer: 1]
這裏與上面的區別在於only
接受的是一個列表, 也就是說, 你還能夠導入其餘你須要指定的函數, 好比下面的代碼僅導入了to_integer/1
和duplicate/2
兩個List
模塊中的函數.
import List, only: [to_integer: 1, duplicate: 2]
最後的require
比較簡單, 其做用相似於C語言中的#include "zlib.h"
把頭文件包含進來, 編譯連接的時候就會去查找對應的共享庫. require
的做用與之相似,要使用其餘模塊所提供的函數和宏, 就必需要require
進來.