Elixirにおけるメタプログラミングの落とし穴


この4つのパートシリーズの最後の部分に戻ってくださいmetaprogramming in Elixir .
以前に,マクロの様々な応用を調べた.
この部分では、エリクサーでメタプログラミングするときに遭遇する可能性があります一般的な落とし穴を掘ります.

マクロの一般的な危険
によるとofficial documentation :

Macros should only be used as a last resort. Remember that explicit is better than implicit. Clear code is better than
concise code.


すべてのためにmetaprogrammingを使用することを誘惑するかもしれない間、それは常に最高のオプションであるかもしれません.
The Applications of Macros このシリーズの一部は、マクロの使用例の大部分を概説します.
ただし、注意してマクロを使用する必要があります.
マクロを使うのを避けるために、これら三つの一般的な落とし穴を見ています.
  • 不要な関数の注入
  • 過剰注射行動
  • 正規関数の置換
  • マクロを持つモジュールに不要な関数を注入した場合、何が起こるかを見て起動しましょう.
    注:これらのポイントはMetaprogramming Elixir .

    1 .マクロで不要な関数を注入する
    マクロは呼び出し元に関数を注入するために使用することができますが、これは不要です.
    例を見てみましょう.
    defmodule CalculatorTransformer do
      defmacro __using__(_) do
        quote do
          def add(a, b), do: a + b
          def subtract(a, b), do: a - b
          def multiply(a, b), do: a * b
          def divide(a, b), do: a / b
        end
      end
    end
    
    defmodule Hospital do
      use CalculatorTransformer
    
      def calculate_cost(suite, procedure) do
        add(suite * 20, multiply(procedure, 5))
      end
    end
    
    我々は様々な電卓機能を注入するHospital モジュール識別子の必要性を排除する.
    ただし、add and multiply 今、彼らは薄い空気から表示されるようです.
    コードはその意味の意味を失い、初めての読者の理解を深める.
    コードの意味的な意味を保持するにはCalculatorTransformer 通常のモジュールとして.このモジュールはimport エドHospital モジュール識別子を削除するには、次の手順に従います
    defmodule CalculatorTransformer do
      def add(a, b), do: a + b
      def subtract(a, b), do: a - b
      def multiply(a, b), do: a * b
      def divide(a, b), do: a / b
    end
    
    defmodule Hospital do
      import CalculatorTransformer
    
      def calculate_cost(suite, procedure) do
        add(suite * 20, multiply(procedure, 5))
      end
    end
    
    不要な機能を作成するのと同様に、マクロもモジュールへの動作を注入することができます.これがどういう意味か調べましょう.

    注入行動上のマクロ
    Elixirが呼び出しサイトにマクロを注入すると、動作はオーバーインジェクションできます.
    の例に戻りましょうBaseWrapper :
    どのような場合は、構文解析ロジックpost? 内部__using__ マクロ?
    defmacro __using__(opts) do
      quote location: :keep, bind_quoted: [opts: opts] do
        # ...
    
        def post?(url, body) do
          case post(url, body) do
            {:ok, %HTTPoison.Response{status_code: code, body: body}} when code in 200..299 ->
              {:ok, body}
    
            {:ok, %HTTPoison.Response{body: body}} ->
              IO.inspect(body)
              error = body |> Map.get("error", body |> Map.get("errors", ""))
              {:error, error}
    
            {:error, %HTTPoison.Error{reason: reason}} ->
              IO.inspect("reason #{reason}")
              {:error, reason}
          end
        end
      end
    end
    
    このアプローチには2つの問題があります.

  • テストによってpost? , あなたは、BaseWrapper .
    複数の継承子が存在するときBaseWrapper そして、post? がInheritorに注入されると、すべての継承子を個別にテストする必要があります.
    これは、任意の継承者固有の動作がpost? .
    そうすることができないならば、テスト範囲を下げることができます.

  • 曖昧エラー報告
    によって発生したランタイムエラーpost? は継承者の下に記録されるBaseWrapper .
  • そのため、post? することができます混乱を作成します.
    の最初の実装BaseWrapper 代わりに、解析処理の大半をラッパーに移動します.この実装は、より意味があり、意味的に意味があり、読みやすい.
    これにより、上記の2つの問題が解決されます.
  • コアの動作をテストするときpost? , のみBaseWrapper.parse_post がテストされている.

  • 構文解析のエラーはBaseWrapper .
    注意:location: :keep 同様の方法で動作します.
  • オーバーインジェクション動作の例ではラッパーを使用していますが、通常のマクロにも同様に適用できます.
    親指のルールはマクロの動作量を最小にすることです.
    マクロを必要とする必要な情報/計算がいったんアクセス/実行されると、残りの動作をマクロから動かす必要があります.
    我々が調べる最終的な落とし穴は、規則的な機能が十分であるとき、マクロの使用です.

    通常の関数の代わりに使用されるマクロ
    として、彼らは常にマクロを必要としない.場合によっては、マクロの動作を通常の関数で置き換えることができます.
    例えば、コンパイル時の情報を必要としない動作(またはマクロを計算するマクロ)がマクロに置かれているとしましょう.
    defmodule Foo do
      defmacro double(x) do
        quote do
          doubled = unquote(x) * 2
          doubled
        end
      end
    end
    
    defmodule Baz do
      require Foo
    
      def execute do
        Foo.double(3)
      end
    end
    
    iex(1)> Baz.execute
    6
    
    こちらです.double 簡単に正規の関数に置き換えることができました.
    その動作はコンパイル時情報や計算のためのマクロを必要としません.注入されるBaz そしてexecute は通常の関数と同じように呼び出されます.
    defmodule Foo do
      def double(x), do: x * 2
    end
    
    defmodule Baz do
      def execute, do: Foo.double(3)
    end
    
    iex(1)> Baz.execute
    6
    
    ご覧の通り、定義double マクロとしては、通常の関数では何の利点もありません.

    Elixirにおけるメタプログラミング
    我々は最終的にエリクサーのメタプロファイラにこの調査の終わりに来ている!
    注意:偉大な力で大きな責任が来る.誤植Metaprogrammingはあなたを噛むために戻ることができますので、軽くトレッド.
    このシリーズは、メタプログラミングとその複雑さを簡潔に説明することを目的としているが、それは、このトピックには、“聖書”という意味ではありません.
    あなたがエリクサーでメタプログラミングについてもっと学ぶために使用できる多くの素晴らしいリソースがあります!ここでほんの数:
    案内書

  • Understanding Elixir Macros - The Erlangelist *
  • Official Elixir tutorial on metaprogramming
  • Metaprogramming by Elixir School
  • Metaprogramming in Elixir - Serokell

  • Metaprogramming Elixir *
  • 協議

  • (*-非常に推奨)
    読書のおかげで、次回あなたを参照してください!
    P . S .あなたが彼らがプレスから降りるとすぐにエリキシル錬金術柱を読みたいならば.subscribe to our Elixir Alchemy newsletter and never miss a single post!
    Jia Hao wooは、リトルレッドドットからの開発者です-シンガポール!彼は様々な技術をいじるのが大好きで、エリキシルを使っていて、およそ1年の間行きます.彼のプログラミング旅行に続いてくださいhis blog および.