matteosister

matteosister

Creating a variable with a macro, and then use it in its body

I’m trying to do something similar to wath ecto does when defining schemas. Basically the idea is:

defmodule Test do
  use TableBuilder

  row "test" do
    field "field1"
    field "field2"
  end
end

row and field are macros defined in the TableBuilder module, what I’m trying to achieve is a DSL that underneath builds a Row struct, that has a fields collection of Field structs

here is what I’m doing right now:

defmodule TableBuilder do
  defmacro __using__(_opts) do
    quote do
      import unquote(__MODULE__)
      Module.register_attribute __MODULE__, :rows, accumulate: true
      @before_compile unquote(__MODULE__)
    end
  end

  defmacro __before_compile__(_env) do
    quote do
      def get_table do
        inspect @rows
      end
    end
  end

  defmacro row(name, do: fields) do
    row = %Row{name: name}
    quote do
      @rows {unquote(row), unquote(fields)}
    end
  end

  defmacro field(name) do
    quote do
      %Field{name: unquote(name)}
    end
  end
end

the problem is:

when the field macro is called, I need to access the row variable created in the row macro.
But I can’t find a solution.

What’s the problem with this approach? Am I doing it wrong? Do you see better approaches to the problem?

Many thanks in advance!

Most Liked

Qqwy

Qqwy

TypeCheck Core Team

By default, Macros in Elixir are unable to access variables outside of their definition. This is called ‘hygienic’. It is also possible to create unhygienic macros, which are what you are looking for here. Saša Jurić’s guide on Macros has some great information on this subject.

matteosister

matteosister

yes! And this is exactly what I’m doing right now.

But this approach do not work perfectly for me for two reasons:

  • I need to define multiple rows in a single module (and right now I’m using the fields attribute and then wipe it right after the macro body invocation to be empty for the next row)

  • the fact that you are not forced in any way to use field inside a row macro, so weird things could happen. But maybe this is also a problem for ecto…and with a good documentation should be ok.

I’d really love to create a struct in the row macro and than pass it (like var!) when unquoting the body, but as far as I know it’s impossible…

matteosister

matteosister

thanks for pointing me there…after many attempts I found a nice solution. Not so clean, but it’s working. :smiley:

Where Next?

Popular in Questions Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
nobody
Hi! In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir? Searched the docs for ip address and the web, no good results. Thanks!
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New
vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New

Other popular topics Top

9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
JorisKok
I have a server on AWS, and was running a load test using artillery. When looking at the Phoenix dashboard I see the Ports going to 100% ...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement