2017-05-04 6 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 

今私は経由のFooを実装することができますが、@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

関連する問題