Elixir 중원 프로그래밍의 함정

16442 단어 elixir
metaprogramming in Elixir에서 우리 네 부분 시리즈의 마지막 부분으로 돌아온 것을 환영합니다.
이전에 우리는 거대한 각종 응용을 탐구했다.
이 섹션에서는 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.


모든 일에 원 프로그래밍을 사용하는 것은 매력적일 수 있지만, 항상 최선의 선택은 아닐 수도 있다.
이 시리즈의 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에 주입할 것이다.
    그러나 addmultiply의 사용은 현재 근거 없이 나타난 것 같다.
    이 코드는 의미를 잃어 처음 읽는 사람에게는 더욱 이해하기 어려워졌다.
    코드의 의미를 보존하려면 CalculatorTransformer을 일반 모듈로 정의하십시오.이 모듈은 importHospital을 삽입하여 모듈 식별자를 제거할 수 있습니다.
    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
    
    불필요한 함수를 만드는 것 외에 매크로는 모듈에 과도하게 주입할 수 있습니다.이것이 무엇을 의미하는지 토론해 봅시다.

    2. 매크로 과잉 주입 행위
    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
    
    이런 방법에는 두 가지 문제가 있다.

  • 테스트 post?을 통해 상속기를 테스트할 수 있으며 BaseWrapper이 아니다.BaseWrapper에 여러 명의 상속자가 있고 post?의 전체 행위가 상속자에게 주입되기 때문에 우리는 각 상속자를 단독으로 테스트해야 한다.
    이는 상속자에 특정한 행위가 post?의 행위를 수정하지 않도록 확보할 수 있다.
    그렇지 않으면 테스트 범위가 낮아질 수 있습니다.

  • 불명확한 오류 보고.post?이 일으킨 모든 운행 시 오류는 BaseWrapper이 아닌 계승기에 기록됩니다.
  • 이에 따라 전체 행위를 post?에 남겨 혼란을 초래할 수 있다.BaseWrapper의 원시적인 실현은 대부분의 해석 행위를 포장기로 옮긴다.이런 실현은 더욱 간결하고 의미가 더욱 의미가 있으며 가독성이 더욱 강하다.
    이렇게 하면 다음과 같이 두 문제를 최소화할 수 있습니다.
  • 에서 post?의 핵심 행위를 테스트할 때 모든 상속자가 아닌 BaseWrapper.parse_post만 테스트한다.

  • 해석 프로세스의 모든 오류는 BaseWrapper에 기록됩니다.
    참고: location: :keep은 비슷한 방식으로 작동합니다.
  • 비록 우리가 과도한 주입 행위의 예시에서 포장기를 사용했지만, 이것은 일반적인 매크로에도 적용된다.
    경험의 법칙은 거대한 행위량을 최소화하는 것이다.
    매크로가 필요한 정보/계산에 액세스/실행되면 나머지 동작을 매크로에서 옮겨야 합니다.
    우리가 연구할 마지막 함정은 일반 함수가 충분할 때 매크로를 사용하는 것이다.

    3. 일반 함수 대신 매크로 사용하기
    비록 그것들의 기능은 강하지만, 항상 매크로가 필요한 것은 아니다.어떤 경우 일반적인 함수로 매크로의 행동을 대체할 수 있습니다.
    컴파일할 필요가 없을 때 정보(또는 매크로 실행 계산)의 동작이 매크로에 배치된다고 가정합니다. 예를 들어,
    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의 메타 프로그래밍:추가 읽기
    우리는 마침내 Elixir 중원 프로그래밍에 대한 조사를 끝냈다.
    기억해라: 위대한 힘에는 위대한 책임이 수반된다.원 프로그래밍을 잘못 사용하면 다시 너를 해칠 수 있으니 조심해서 행동해야 한다.
    이 시리즈는 원 프로그래밍과 그 복잡한 점을 간단명료하게 설명하는 데 목적을 두고 있지만, 결코 이 주제의 성경이 아니다.
    아주 좋은 자원이 많아서 Elixir 중원 프로그래밍에 대한 지식을 더 많이 배울 수 있습니다!다음은 다음과 같습니다.
    서면 지침서

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

  • Metaprogramming Elixir *
  • 담화

  • (*- 강력 추천)
    읽어주셔서 감사합니다. 다음에 또 만나요!
    또한, 출판 후 바로 장생불로약 연금술 게시물을 읽고 싶다면 subscribe to our Elixir Alchemy newsletter and never miss a single post!으로 전화하세요
    가호우는 샤오홍점 싱가포르에서 온 개발상이다!그는 각종 기술을 보완하는 것을 좋아했고, 이미 불로장생약과 바둑을 약 1년 동안 사용했다.그의 프로그래밍 여행에 따라 his blog과 전화를 걸었다.

    좋은 웹페이지 즐겨찾기