Elixir 메타 프로그래밍 정보

원 프로그래밍은 무서운 단어이다. 프로그래머의 무독처럼 들리지만, 어떤 면에서는 그렇다.그러나 가장 유행하는 장생약 라이브러리 (Ecto, Phoenix, Elixir 자체) 에서 사용되고 있다. 내가 장생약 프로그래머로 진급할 때, 나는 그것이 배후에서 어떻게 일을 하는지 알아야 한다.
나는 개인적으로 이 화제에 대해 두려움을 느낀다. 진정으로 그것을 읽기 전에, 나는 Metaprogramming Elixir book1년여의 시간을 가지고 있다😱
나의 장생불로약 개인 도서관
그러나 경험이 풍부한 장생불로약 프로그래머에게는 아주 좋은 수첩으로 읽기에 매우 즐겁다.또한 Chris McCord의 권장 사항에 100% 동의합니다.

Rule 1 : Don't Write Macros - Chris McCord


원 프로그래밍은 유지하기 어렵고 디버깅하기 힘들기 때문에 조심해야 한다.심지어 그것으로 응용 프로그램의 핵심 기능을 작성하는 것도 고려하지 마라.기술 라이브러리, 사용자 정의 DSL 및 기타 특정 주제에 더 적합합니다.

이것은 일종의 마력이다


여러분과 마찬가지로 저는 Ruby on Rails 개발 배경에서 왔습니다. 저는 Rails를 공부할 때 언어의 표현력과 신기한 특성에 대해 인상적이었던 것을 기억합니다.Java에서 온 배경은 저에게 축복입니다!
irb> 2.years.from_now + 5.days.from_now 
#=> Mon, 28 Nov 2022 13:38:36 UTC

irb> Person.find_by_user_name_and_password(user_name, password)
#=> #<Person id: 10, user_name: "Ryan">
루비 마법
Rails는 런타임 시 언어를 확장하기 위해 Ruby 동적 특성을 많이 사용합니다. 위의 find_by_user_name_and_password 호출은 Person 모델 속성을 바탕으로 동적 생성된 클래스 방법의 예입니다.
Ruby on Rails를 배울 때의 느낌
그러나 루비 매직에는 다음과 같은 경고가 있습니다.

  • too much magic 엔진 뚜껑에 무슨 일이 일어났는지 몰라서 다칠 수도 있어요.같은 논리도 Elixir 메타 프로그래밍에 적용됩니다(Chris McCord의 좌우명을 기억하십니까? "매크로를 작성하지 마세요."😅)
  • 루비 매직은 본질적으로 동적이며 이는 성능을 희생하는 대가로 실행할 때 실행된다는 것을 의미한다.Elixir 매크로는 컴파일할 때 계산하고 바이트 코드를 삽입합니다. 마치 수동으로 작성한 코드와 같습니다.
  • 무엇이 원 프로그래밍입니까?


    이 주제를 깊이 연구하기 전에 원 프로그래밍은 코드를 생성하는 코드를 작성하는 예술이다.메타 프로그래밍 기능은 Elixir 언어의 일류 구성원으로 핵심 언어를 빠르게 확장할 수 있는 매크로 API를 제공합니다.
    당신은 if-then-else 또는 블록 문법이 그 매크로 메커니즘을 통해 Elixir로 작성되지 않은 것을 아십니까?그것은 이원 대소문자 패턴의 일치 구조를 통해 순조로운 설탕을 제공한다.
    defmacro macro_unless(clause, do: expression) do
      quote do
        if(!unquote(clause), do: unquote(expression))
      end
    end
    
    Elixir 매크로로 블록에 쓰지 않는 한
    너도 매일 부지불식간에 메타프로그래밍 기능을 사용할 수 있다.

  • ExUnit 테스트 정의 활용 매크로

  • Ecto querying DSL 메타 프로그래밍을 통해 SQL 언어를 완벽하게 모방
  • 물론 Chris McCord의Phoenix 프레임워크는 매크로를 자주 사용한다. 예를 들어Routing DSL
  • 코드의 90%가 Elixir로 작성된 거 알아?그만해?🤯

    장생불로약홍101


    첫 번째 불로장생 약홍을 쓰는 것은 좀 지루할 것이다.나는 Elixir 원 프로그래밍에 관한 완전한 과정을 제공하지 않을 것이다(구매Chris McCord’s book!)그걸 이해할 수 있는 방법을 알려줄게.
    탓할 수 없어, 나한테도 일어났어!
    Elixir에서 작성한 모든 코드가 메모리에서 Abstract Syntax Tree,AST로 표현된다는 점을 알아야 합니다.컴파일러는 코드를 해석하여 AST로 변환한 다음 BEAM은 AST를 계산하여 그것을 실행합니다.
    추상 문법 트리(AST)
    원 프로그래밍은 우리가 이전에 정의한'코드를 생성하는 코드'로 원시적인 불로장생약 코드(문자열로)를 생성하여 실현할 수 있으나 효율이 낮아 유지하기 어렵다.
    이것이 바로 Elixir가 API를 제공하여AST를 생성하고 평가하는 이유입니다.다음은 quote: 모든 Elixir 코드를AST로 변환할 수 있는 Elixir 원어입니다.
    iex(1)> quote do 5 + 8 * 3 end
    #=> {:+, [context: Elixir, import: Kernel],
    #=> [5, {:*, 
    #=> [context: Elixir, import: Kernel], 
    #=> [8, 3]
    #=> ]}]
    #=> }
    
    이 코드의 값 구하기 방식은 문자열의 코드 값 구하기 방식과 같다Javascripteval
    iex(1)> Code.eval_string("5 + 8 * 3")
    #=> {29, []}
    iex(2)> Code.eval_quoted(quote do 5 + 8 * 3 end)
    #=> {29, []}
    
    현재 동적 부분 평가 코드를 사용하려고 시도하면 어떤 일이 일어날까요?
    iex(1)> a = 5
    iex(2)> Code.eval_string("3 * a")
    #=> ** (CompileError) nofile:1: undefined function a/0
    
    이것은 당신의 텍스트를 복구하는 간단한 방법입니다.
    iex(1)> a = 5
    iex(2)> Code.eval_string("3 * #{a}")
    #=> {15, []}
    
    좋은 소식은 인용된 코드에 삽입할 수 있습니다. a 원어를 사용하십시오.
    iex(1)> a = 5
    iex(2)> Code.eval_quoted(quote do 3 * unquote(a) end)
    #=> {15, []}
    
    이렇게 간단해!그래서 unquote/quote 블록 사이를 잃었다고 느낄 때마다 텍스트 삽입값으로 보기만 하면 된다.
    그리고 봉황성 프로젝트에서 만났을 수도 있다는 것을 알아야 한다unquote.그것은 인용된 표현식과 파라미터로부터 코드를 생성할 수 있는 특수한 구조이다.
    다음은 몇 개의 함수를 생성하는 예입니다.
    defmodule MyMacro do
      defmacro generate_functions(name_and_values) do
        for {name, value} <- name_and_values do
          quote do
            def unquote(name)(), do: unquote(value)
          end
        end
      end
    end
    
    defmodule MyModule do
      require MyMacro
    
      MyMacro.generate_functions([{:one, 1}, {:two, 2}, {:three, 3}])
    end
    
    iex(1)> MyModule.two + MyModule.three 
    #=> 5
    
    Elixir 원 프로그래밍에 대해서는 더 많은 것을 알아야 하지만 defmacro, quoteunquote는 기초로 강력한 것을 구축하는 데 도움을 줄 수 있다.

    그래, 그런데 왜?


    이 글의 마지막 부분에서, 나는 우리가 Elixir 프로젝트에서 메타 프로그래밍을 사용하는 방식을 예를 들어 설명할 것이다.

    EXTO 쿼리 API 확장


    Ecto는 SQL을 모방하는 완벽한 쿼리 API를 제공합니다.Elixir 매크로는 질의에 오류가 발생할 경우 컴파일 오류를 트리거하는 데 사용됩니다.
    iex(1)> query = from u in User, where: u.age > 0, select: u.name
    iex(2)> Repo.all(query)
    
    일반적인 외부 쿼리
    그러나 Ecto API에서 사용할 수 없는 특정 데이터베이스 기능을 사용하려는 경우도 있습니다.그런 다음 EXTO 질의에 원본 SQL을 삽입할 수 있는 defmacro EXTO 함수를 사용할 수 있습니다.
    query = 
      from u in User,
      where: is_nil(fragment("?->>?", u.metadata, "phone"))
    
    Postgres JSONB 열에 fragment를 사용하여 필드 액세스
    그러나 정확한 방법은 매크로를 사용하여 EXTO 쿼리 API를 확장하는 것입니다.
    defmodule MyQueryMacros do
      defmacro jsonb_get(column, key) do
        quote do
          fragment("?->>?", unquote(column), unquote(key))
        end
      end
     end
    
     query = 
       from u in User,
       where: is_nil(jsonb_get(u.metadata, "phone"))
    
    자체 제작된 jsonb get 매크로를 사용하여 동일한 EXTO 질의를 수행합니다.

    별칭 및 가져오기


    Elixir 코드 라이브러리의 증가에 따라 파일의 시작에 같은 fragment 블록과 alias 블록을 반복하는 것을 볼 수 있습니다.진짜 아니에요DRY...
    Phoenix는 import/use 매크로를 통해 사용할 수 있는 솔루션을 제공합니다.컨트롤러나 보기를 설명하려면phoenix는 __using__ 파일에 설명된 기본 별명과 가져오는 것을 권장합니다.다음은 changelog.commy_app_web.ex 파일의 예입니다. changelog.com/changelog_web.ex file
    그런 다음 매크로를 다음 코드 세그먼트와 함께 사용하면 여러 Phoenix 모듈의 앨리어스와 라이센스 관련 함수를 생성할 수 있습니다.
    defmodule MyAppWeb.MyController do
      use MyAppWeb, :controller
    
      # ...
    end
    
    우리 프로젝트에서, 우리는 또 자신의 별명 시스템을 준비했다. 우리는 이렇게 그것을 사용해서 우리가 같은 별명을 반복적으로 성명하는 것을 방지할 수 있다.
    defmodule MyApp.Contracts.SomeContractService do
      use MyApp,
        aliases: [:campaigns, :contracts, :users],
        private_aliases: [:contracts]
    
      # ...
    end   
    
    나는 다른 문장에서 이러한 특정한 웅장함을 한층 더 소개할 수 있다😉

    changix


    주요 Elixir 응용 프로그램에서 매크로를 사용하는 방법의 마지막 예는 응용 프로그램 변경 로그입니다.우리는 changelog 사이드바 (깜빡이는 휘장) 를 통해 사용자에게 새로운 기능, 오류 복구, 개선을 보여 줍니다.
    저희 네트워크 프로그램에 삽입된changelog 사이드바
    데이터베이스에서 이 변경 로그를 입력하는 대신 YAML front matter 헤더가 있는 태그 파일을 사용합니다.
    저희 가격 인하 변경 로그 파일은 원본 코드에 따라 제출됩니다.
    처음에 사용자가 사이드바를 트리거할 때마다 런타임에 이러한 파일을 분석한 다음 성능을 향상시키기 위해 HTML 출력을 메모리에 캐시합니다.
    그러나 Chris McCord의 책을 읽은 후에 우리는 이러한 태그 파일들이 원본 코드로 볼 수도 있고 컴파일될 수도 있다는 것을 깨달았다. (적어도 컴파일할 때 처리될 수도 있다.)다음은 changix: 컴파일할 때 로그 기능을 변경하는 매크로에 의존하는 라이브러리입니다.
    cblavier/changix on Github

    마지막 말


    나는 이 글이 당신이 Elixir 원 프로그래밍에 대해 더욱 편안함을 느끼고, 당신의 원 프로그래밍을 해독할 수 있는 충분한 용기를 줄 수 있기를 바랍니다.하지만 크리스의 조언을 기억하고 항상 신중하세요.😅

    좋은 웹페이지 즐겨찾기