2017-05-04 42 views
1

我有一個具有單一功能具有相對複雜的API協議:實現與行爲的協議中的一個宏在藥劑

defprotocol Foo do 
    def complex(foo, x, y) 
end 

我想提供一種方法來實現此協議爲共同和更簡單的用例。一些實驗後,我想出了以下內容:

defmodule Bar do 
    @callback simple(any, any) :: boolean 

    defmacro __using__(_) do 
    quote do 
     @behaviour Bar 

     defimpl Foo do 
     # TODO this is really hacky. What is a better way to reference parent module? 
     # Note that the 1 in drop(1) is from Foo's module name 
     @parent_module __MODULE__ |> Module.split |> Enum.drop(1) |> Module.concat 
     def complex(bar, x, _y) do 
      matches = @parent_module.simple(bar, x) 
      if matches, do: [x], else: [] 
     end 
     end 
    end 
    end 
end 

現在我可以通過實現富,但@parent_module確定和使用的方式似乎是錯誤的。有沒有更好的辦法比上面的TODO使用黑客?做這件事的慣用方法是什麼?我寧願將Foo變爲行爲,因爲協議的分派和合並符合使用模式。

+0

我實在不明白你試圖解決的問題。你能提供一個複雜而簡單的用例嗎?令我困惑的是沒有'for:'選項的defimpl的使用。 –

+1

'for'參數默認爲定義'defimpl'的模塊 –

+0

爲什麼不只是'__MODULE__'工作? –

回答

3

你可以在一個局部變量存儲父模塊的名稱,然後把它放在一個屬性的實現,然後使用它:

defmodule Bar do 
    @callback simple(any, any) :: boolean 

    defmacro __using__(_) do 
    quote do 
     @behaviour Bar 

     parent_module = __MODULE__ 

     defimpl Foo do 
     @parent_module parent_module 

     def complex(bar, x, _y) do 
      matches = @parent_module.simple(bar, x) 
      if matches, do: [x], else: [] 
     end 
     end 
    end 
    end 
end 
+0

太棒了! 「parent_module」變量是否實際存在於生成的結果模塊中,還是僅存在於宏中?我問,因爲我認爲你不能在不是模塊屬性的模塊中聲明這樣的變量。 –

+1

它將出現在生成的代碼中,但由於宏衛生,它不能從名爲'parent_module'的調用模塊的其餘部分訪問。它不會出現在運行時,只在編譯時。 – Dogbert