Elixir - Macro

Macro là một trong những tính năng tiên tiến và mạnh mẽ nhất của Elixir. Như với tất cả các tính năng nâng cao của bất kỳ ngôn ngữ nào, macro nên được sử dụng một cách tiết kiệm. Chúng làm cho nó có thể thực hiện các chuyển đổi mã mạnh mẽ trong thời gian biên dịch. Bây giờ chúng ta sẽ hiểu sơ lược về macro là gì và cách sử dụng chúng.

Trích dẫn

Trước khi bắt đầu nói về macro, trước tiên chúng ta hãy xem xét nội bộ của Elixir. Một chương trình Elixir có thể được biểu diễn bằng cấu trúc dữ liệu của chính nó. Khối xây dựng của một chương trình Elixir là một bộ gồm ba phần tử. Ví dụ, lệnh gọi hàm sum (1, 2, 3) được biểu diễn bên trong dưới dạng:

{:sum, [], [1, 2, 3]}

Phần tử đầu tiên là tên hàm, phần tử thứ hai là danh sách từ khóa chứa siêu dữ liệu và phần tử thứ ba là danh sách đối số. Bạn có thể lấy nó làm đầu ra trong iex shell nếu bạn viết như sau:

quote do: sum(1, 2, 3)

Các toán tử cũng được biểu diễn dưới dạng các bộ giá trị như vậy. Các biến cũng được biểu diễn bằng cách sử dụng các bộ ba như vậy, ngoại trừ phần tử cuối cùng là một nguyên tử, thay vì một danh sách. Khi trích dẫn các biểu thức phức tạp hơn, chúng ta có thể thấy rằng mã được biểu diễn trong các bộ như vậy, chúng thường được lồng vào nhau trong một cấu trúc giống như một cái cây. Nhiều ngôn ngữ sẽ gọi các biểu diễn như vậy làAbstract Syntax Tree (AST). Elixir gọi những biểu thức được trích dẫn này.

Bỏ trích dẫn

Bây giờ chúng ta có thể truy xuất cấu trúc bên trong của mã của mình, làm cách nào để sửa đổi nó? Để nhập mã hoặc giá trị mới, chúng tôi sử dụngunquote. Khi chúng tôi hủy trích dẫn một biểu thức, nó sẽ được đánh giá và đưa vào AST. Chúng ta hãy xem xét một ví dụ (trong iex shell) để hiểu khái niệm -

num = 25

quote do: sum(15, num)

quote do: sum(15, unquote(num))

Khi chương trình trên được chạy, nó tạo ra kết quả sau:

{:sum, [], [15, {:num, [], Elixir}]}
{:sum, [], [15, 25]}

Trong ví dụ cho biểu thức trích dẫn, nó không tự động thay thế num bằng 25. Chúng ta cần hủy trích dẫn biến này nếu chúng ta muốn sửa đổi AST.

Macro

Vì vậy, bây giờ chúng ta đã quen thuộc với trích dẫn và hủy trích dẫn, chúng ta có thể khám phá lập trình ẩn trong Elixir bằng cách sử dụng macro.

Nói một cách đơn giản nhất, macro là các hàm đặc biệt được thiết kế để trả về một biểu thức được trích dẫn sẽ được chèn vào mã ứng dụng của chúng tôi. Hãy tưởng tượng macro được thay thế bằng biểu thức được trích dẫn thay vì được gọi giống như một hàm. Với macro, chúng tôi có mọi thứ cần thiết để mở rộng Elixir và thêm mã động vào các ứng dụng của mình

Hãy để chúng tôi triển khai trừ khi là một macro. Chúng ta sẽ bắt đầu bằng cách xác định macro bằng cách sử dụngdefmacrovĩ mô. Hãy nhớ rằng macro của chúng ta cần trả về một biểu thức được trích dẫn.

defmodule OurMacro do
   defmacro unless(expr, do: block) do
      quote do
         if !unquote(expr), do: unquote(block)
      end
   end
end

require OurMacro

OurMacro.unless true, do: IO.puts "True Expression"

OurMacro.unless false, do: IO.puts "False expression"

Khi chương trình trên được chạy, nó tạo ra kết quả sau:

False expression

Điều đang xảy ra ở đây là mã của chúng tôi đang được thay thế bằng mã được trích dẫn được trả về bởi macro trừ khi . Chúng tôi đã bỏ trích dẫn biểu thức để đánh giá nó trong ngữ cảnh hiện tại và cũng bỏ trích dẫn khối do để thực thi nó trong ngữ cảnh của nó. Ví dụ này cho chúng ta thấy lập trình siêu ứng dụng bằng macro trong elixir.

Macro có thể được sử dụng trong các tác vụ phức tạp hơn nhiều nhưng nên được sử dụng một cách tiết kiệm. Điều này là do lập trình ẩn nói chung được coi là một thực hành không tốt và chỉ nên được sử dụng khi cần thiết.