Elixir: use, import, 和 require

概述

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/1List.to_integer/2兩個名爲to_integer的函數, 若是你只想導入to_integer/1,那麼能夠按下面的方式來導入:編譯

import List, only: [to_integer: 1]

這裏與上面的區別在於only接受的是一個列表, 也就是說, 你還能夠導入其餘你須要指定的函數, 好比下面的代碼僅導入了to_integer/1duplicate/2兩個List模塊中的函數.

import List, only: [to_integer: 1, duplicate: 2]

最後

最後的require比較簡單, 其做用相似於C語言中的#include "zlib.h"把頭文件包含進來, 編譯連接的時候就會去查找對應的共享庫. require的做用與之相似,要使用其餘模塊所提供的函數和宏, 就必需要require進來.

相關文章
相關標籤/搜索