Eiji

Eiji

Is evaluating a quoted expressions of nested DSL calls also considered as a bad practice?

Warning: Calling this function inside a macro is considered bad practice as it will attempt to evaluate runtime values at compile time. Macro arguments are typically transformed by unquoting them into the returned quoted expressions (instead of evaluated).

Source: Code.eval_quoted/3

I’m not sure if I understand it fully. Is it only about “runtime values” (like variables)? Does it apply also to do: block last argument for nested DSL?

For simplicity let’s assume that you start with a simplest DSL, but with 1 extra nested DSL. You prefer do … end way because it’s more flexible, so for example you can use a for … do … end special form to generate nested DSL calls based on some input. Consider below example:

defmodule MyLib do
  defmacro first_case_root(do: block) do
    quote do
      unquote(block)
    end
  end

  defmacro second_case_root(do: block) do
    Code.eval_quoted(block, [], __CALLER__)
  end

  defmacro nested do
    nil
  end
end

defmodule MyApp do
  import MyLib

  first_case_root do
    nested()
  end

  second_case_root do
    nested()
  end
end

In first case the nested macro is called when unquoting a result of the parent macro. For simplicity let’s say it’s called between first_case_root and second_case_root. This way looks a bit complicated as to store accumulated arguments we have to work in inner macro (inside quote) and/or on @before_compile.

However I found something obvious … I can just evaluate quoted block (as shown in second case). This way the nested DSL is evaluated inside a parent macro which in fact flattens nested DSL calls. This allows to work on the parent macro logic inside … a parent macro.

Now let’s go back to the quote. I understand why AST of stuff like variables should not be evaluated especially because in many macros it’s completely not important. However in this case “flattening” nested DSL calls allows to simplify code a lot:

  1. Even in simplest example it’s 1 LOC vs 3 LOC where one line have extra indention.

  2. There is no need to work on module attributes to store compile-time data. Instead we can use many if not all in-memory solutions. The simplest one is most probably Process.put/2

  3. Depending on use case the nested DSL macros does not have to return any quoted expressions and the root DSL macro returns a minimal quoted expression without storing and manipulating data

{nil, []} = Code.eval_quoted(block_with_nested_dsl_calls, [], __CALLER__)

Both macro code and the code returned by macro are much simpler. Simply imagine that a root macro with dozens or even more nested macro calls returns a minimal code which you print like tap(& &1 |> Macro.to_string() |> IO.puts()) and the output contains only desired generated code (generated functions) without lots of other lines you have to scroll in console.

Does described case is also seen as a bad practice? Or as written above the warning was intended only for “runtime values” like variables? If this is bad idea could you please explain why? Are there any side-effects of doing so?

Marked As Solved

D4no0

D4no0

I mean that is just a generic warning, you can do whatever you want in your macros.

I personally would say that using Code.eval_quoted/3 inside of your macros is an imperative way of doing things and composing the AST is the declarative way, as you are composing a data structure that is later evaluated.

I don’t have experience doing this kind of shenanigans, however I would suspect you running into trouble with evaluation contexts sooner or later when you are doing this kind of intermediary evaluations of code.

Where Next?

Popular in Questions Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
nobody
How to bind a phoenix app to a specific ip address? could not find anything about that, nowhere, unfortunately, but for me this is quite...
New
beno
I will often find my self writing things similar to: case some_value do nil -> something() "" -> something() _ -> somethi...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
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
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
komlanvi
Hi everyone, I was playing with phoenix liveView but I run into an issue. I have a form and want to validate each input text when the te...
New
yawaramin
In the Dialyzer docs ( dialyzer — OTP 29.0.2 (dialyzer 6.0.1) ), there is a way to turn off a specific warning for a function: -dialyzer...
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

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
danschultzer
None of the current solutions worked well for me, so I went ahead and built a user management system from scratch. This project took far...
548 29603 241
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
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
belgoros
I’m not a pro in using Regex and can’t figure out why the following behaviour happens, especially if we take into account the difference ...
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers’ Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement