feld

feld

I have this situation where I have a list of modules in a module attribute to be used for another purpose and I also want to “use” them, so I thought it would be nice to have a single place in the module to enable/disable their functionality. Unfortunately I have been unable to find a way to make it work, but it doesn’t provide me any errors.

Is there any way to make this work as I expect?

defmodule Example do

@mods [
  Example.One
  Example.Two
]

def something() do
  # When called, enumerate over @mods here for another purpose
end

# this doesn't work
for mod <- @mods, do: quote do: use unquote(mod)

end

It doesn’t error, but it definitely is not calling the __using__ of that module. Explicitly setting the use works, even if you set it after this, so it’s definitely not working.

Any thoughts?

Showing Posts 1 to 10

zachdaniel

zachdaniel

Creator of Ash
for mod <- @mods, do: quote do: use unquote(mod)

is equivalent to

for mod <- @mods do 
  quote do 
    use unquote(mod)
  end
end

Note that the quote do there just causes the AST A.K.A “quoted code” to be returned. Nothing is actually evaluating it.

Try this (haven’t verified that it works). In a different module, make a macro called use_all.

defmodule SomeModule do
  # explicitly expects a module attribute to be given to it
  defmacro use_all({:@, _, [{name, _, _}]}) do
      for module <- Module.get_attribute(__CALLER__.module, name, []) do
        quote do
           use unquote(module)
        end
      end
    end
  end
end

Then in your module you can use

SomeModule.use_all(@attr)
zachdaniel

zachdaniel

Creator of Ash

There is a simpler way that is perhaps a bit less hygienic as its typically recommended to avoid Code.eval_quoted, but you could do this:

for mod <- @mods do
  quote do
    use unquote(mod)
  end
  |> Code.eval_quoted([], __ENV__)
end
feld

feld OP

Thanks! I knew I was on the right track and I was staring at the source code for use in the Kernel but hadn’t remembered that the AST I was generating wasn’t being evaluated…

I haven’t tested the more complex solution but I’ll give it a spin tomorrow and report back for future readers.

zachdaniel

zachdaniel

Creator of Ash

Honestly I think it may just be the right way. I’m not sure why I was thinking one should avoid Code.eval_quoted, but I think that usage of it is perfectly reasonable.

sodapopcan

sodapopcan

I can’t get this one to work. Isn’t this going to result in each use being scoped to for’s block? I have a poor mental model of using quote directly in a module body, though I was trying other stuff earlier (somewhat akin to your first answer, though not quite) and no luck there either :cold_sweat: I actually can’t get the first example working either, says that @mods is unused.

feld

feld OP

Oh that would be funny if I found a bug in the compiler or something. I’m already using that @mods equivalent in another function so it wouldn’t error as unused. See if that solves it for you?

zachdaniel

zachdaniel

Creator of Ash

You’re right :slight_smile:

Here is a working example:

defmodule Foo do
  defmacro __using__(_) do
    quote do
      import Integer
    end
  end
end

defmodule MultiUse do
  # must be a literal list
  defmacro __using__(mods) do
    using_code =
      for module <- Macro.expand_literals(mods, __CALLER__) do
        quote do
          use unquote(module)
        end
      end

    setter = quote do
      @using_modules unquote(mods)
    end

    [setter | using_code]
  end
end

defmodule Bar do
  use MultiUse, [Foo]
  @using_modules |> IO.inspect()
  digits(12)
end

This demonstrates that you can use the list again later w/ @using_modules, and things like import affect the top level scope.

sodapopcan

sodapopcan

Yes, sorry, I shouldn’t have focused on the warning. It doesn’t accomplish the use for me is the main thing. I could be doing it wrong though, it’s getting a bit late for me.

sodapopcan

sodapopcan

Oh, this is clever! This code (without the @using_modules part) is very close to what I had working with the problem being that it failed passing it a module attribute. Instead of expand_literals, though, I used expand_once. Is there a big difference here? I never quite understood the docs of expand_literals though it actually sounds like might be equivalent in this case? Both have to do with expanding aliases properly?

zachdaniel

zachdaniel

Creator of Ash

I think they’re equivalent here, yeah.

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
Onor.io
I have what I’ve heard referred to as a “lookup table” in my database. This is a way of assigning codes to common values. One common lo...
New
jaybe78
Hello, I’m developing a online persistent chat system (what’s app) like using elixir/dynamodb/aws for a mobile app(flutter). The diffic...
New
Trolleger
What approach to take when sending live updates to “random” users Hi! I have a question, I have a little chat app, and when I create a DM...
New

Other Trending Topics Top

garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
wintermeyer
There are three potential reasons for members of this forum to have a look at https://vutuv.de You are tired or annoyed of LinkedIn. Yo...
New
aseigo
ICal is a library for interacting with iCalendar data. It parses iCalendars into typed Elixir structs via ICal.from_ics, and can prepare ...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews