defmacro typedstruct(do: ast) do
fields_ast =
case ast do
{:__block__, [], fields} -> fields
field -> [field]
end
Enum.map(fields_ast, fn field ->
IO.inspect(field) # I could not be able to convert it
end)
end
For example the pattern of ast is not equal always to let me create a pattern, and some times user puts String.t() some times they put list(string()), again it is different
It should be noted, at first I just want to create something like this and after that I want to create t() type and struct inside a module. but I need to go step by step.
after that unquote(block) to let use this as original schema not ast, okey?
it calles @enforce_keys and @ts_enforce_keys, with this part of code every struct should be created with @ts_enforce_keys, without it, the code has to raise an error
it wrote defstruct @ts_fields; it is just located inside @ts_enforce_keys and registered
#defmodule TypedStruct do
#defmacro __using__(_) do
#quote do
# import TypedStruct, only: [typedstruct: 1]
#end
# end
defmacro typedstruct(do: block) do
ast = TypedStruct.__typedstruct__(block)
quote do
# Create a lexical scope.
(fn -> unquote(ast) end).()
end
end
def __typedstruct__(block) do
quote do
# --------------------
Enum.each(unquote(@accumulating_attrs), fn attr ->
Module.register_attribute(__MODULE__, attr, accumulate: true)
end)
# --------------------
import TypedStruct
unquote(block)
@enforce_keys @ts_enforce_keys
defstruct @ts_fields
end
end
defmacro field(name, type, opts \\ []) do
quote bind_quoted: [name: name, type: Macro.escape(type), opts: opts] do
# IO.inspect(type)
end
end
end
As my understanding, he creates some global var under module as module attributes and each step it push data on them, okey?
But where he put each of field to a struct?
typedstruct do
field(:name1, type: String.t(), required: true)
field(:name2, type: DateTime.t(), required: false, default: "shahryar")
field(:name3, type: list(string()), required: true)
end
The code of field:
And after binding all the data I can use it in this line? defstruct @ts_fields, am I right?
And still this part is magic for me why he uses anonymous function
I asked ChatGPT and I do not know it is true or not!! or is there another way to execute:
In Elixir, the quote macro is used to construct an abstract syntax tree (AST) from the code enclosed within it. The unquote macro is used to interpolate a value or expression into the AST being constructed.
When you use unquote(ast) directly within quote do ... end, it would simply insert the value of ast at that point in the AST. However, if you want to execute code and include the result in the AST, you can wrap it in a function and use the () to invoke that function.
In the case of (fn -> unquote(ast) end).(), the purpose is to execute the code represented by ast and include the result in the AST being constructed by the quote macro. By wrapping unquote(ast) inside an anonymous function (fn -> ... end) and immediately invoking it with (), the code is executed and its result is included in the AST.
This technique is often used when you want to include the result of a computation or expression, rather than the actual code itself, in the generated AST. It allows for dynamic generation of code within macros or other code-generation scenarios.
This “explanation” is plausible-sounding nonsense, which is the main output of LLMs.
A better way to answer “why does this code exist” questions, IMO, is to read the notes and discussion from the authors. For the “anonymous function” bit, here’s the PR that changed that code to its present-day form:
I had originally put this to avoid the field macro to be available outside of the typedstruct block, which would be obviously not semantically correct.
The intent of the anonymous function is to prevent the import TypedStruct that TypedStruct.__typedstruct__'s generated AST includes from leaking into the module that said typedstruct do.
It’s not required when the module option is given, since then the import is scoped to the generated module.
I have a huge question, how he could find this solution for his problem? I need that way he tested and found this way. is there a specific concept when we want to debug or maintenance a macro?