tmbb

tmbb

Error in macro that defines a module with a macro inside

I have some relatively legitimate reasons do do something like this (even if you think this is not even remotely legitimate, the question at the end is independent from this):

defmodule MySnippet do
  defmacro __using__(_) do
    quote do
      def common_function1() do
        # ...
      end

      def common_function2() do
        # ...
      end
      # etc...
    end
  end
end

defmodule MyModule1 do
  use MySnippet

  def specific_function1() do
    # ...
  end

  def specific_function2() do
    # ...
  end
  # etc...
end

defmodule MyModule2 do
  use MySnippet

  def specific_function_a() do
    # ...
  end

  def specific_function_b() do
    # ...
  end
  # etc...
end

I’ve thought about defining a defsnippet macro that exapnds so that I coould do this:

require Snippet
Snippet.defsnippet MySnippet do
  def f(x), do: x
  def g(x), do: x
end

This way, it looks like a normal module definition and is easier to type.
I’ve written the following macro, which tries to encapsulate the module definition and the __using__/1 macro definition.

defmodule Snippet do
  defmacro defsnippet(module_name, [do: body]) do
    quote bind_quoted: [module_name: module_name, body: body] do
      defmodule module_name do
        @doc false
        defmacro __using__(_opts) do
          quote do
            body
          end
        end
      end
    end
  end
end

But when I try to run it like this

# lib/schism/example.ex
require Snippet
Snippet.defsnippet MySnippet do
  def f(x), do: x
  def g(x), do: x
end

I get this error:

== Compilation error in file lib/schism/example.ex ==
  ** (ArgumentError) cannot invoke def/2 outside module
      (elixir) lib/kernel.ex:5142: Kernel.assert_module_scope/3
      (elixir) lib/kernel.ex:3905: Kernel.define/4
      (elixir) expanding macro: Kernel.def/2
      lib/schism/example.ex:4: (file)
      expanding macro: Snippet.defsnippet/2
      lib/schism/example.ex:3: (file)

I don’t understand where this comes from, because I’m not calling def/2 outside of a module. I’m just taking some AST that happens to contain defs and putting it inside a macro which I can invoke later.

Doing all of this just to save some typing is probably not worth it and I don’t know if I’ll end up going that way, but no I’m curious about what the problem is independently of the rest.

Marked As Solved

OvermindDL1

OvermindDL1

Eh I think defmodule is fine here.

However the issue is that you cannot use bind_quoted in this way (I learned that well long ago). Basically what bind_quoted will do is turn the code into something like this from the return of your macro:

module_name = MySnippet

body =
  (
    def f(x), do: x
    def g(x), do: x
  )

defmodule(module_name) do
  @doc false 
  defmacro(__using__(_opts)) do
    quote do
      body
    end
  end
end

And this is why you are getting the def is not allowed here message. I’ve always had to do ‘fun’ unquote messing when I had to do similar things. So if you Macro.escape the body in bind_quoted then use unquote(body) inside the function then it might work (you might need to ‘escape’ the inner quote though or something like that, I usually just don’t use bind_quoted at all in this case and just unquote through the layers as needed or build the AST manually without quote…).

Also Liked

tmbb

tmbb

It doesn’t. In fact, defmodule doesn’t have anything to do with it. OverminDL1 has given the correct answer. Thanks for your input anyway!

Where Next?

Popular in Questions Top

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
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
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
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
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
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
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
earth10
Hi, I’m just starting to build a side-project with Elixir and Phoenix and doing some basic test with Elixir alone. What strikes me is th...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Other popular topics Top

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
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
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
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
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
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
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

We're in Beta

About us Mission Statement