sodapopcan
I can dynamically define functions in a “loop” like this:
defmodule Foo do
for {f, i} <- [{:foo, "foo"}, {:bar, "bar"}] do
def foo(unquote(f)) do
unquote(i)
end
end
end
iex> Foo.foo(:foo)
"foo"
iex> Foo.foo(:bar)
"bar"
iex> Foo.foo(:baz)
💣
So that’s all cool ![]()
Now how do I do this inside of a macro? I’ve been at this for a few hours now and having a very hard time wrapping my head around it. The closest I’ve come is this:
defmodule Foo do
defmacro __using__(_) do
quote do
for {f, i} <- [{:foo, "foo"}, {:bar, "bar"}] do
defmacro foo(f) do
quote do
# unquote(i) <- This doesn't work so I'm commenting it out for now
end
end
end
end
end
end
This has the effect of defining def foo(:foo) twice, even though I dbg(f) in inside the inner macro and it has the correct values. On top of that, I can’t access i inside the inner macro.
I got a bit of hint from this thread. I tried re-quoting that whole comprehension, capturing it in a variable, then quote(do: unquote_splicing(block)) it, but that didn’t work. There is more code that follows it so, I dunno.
I appreciate any help though if this serves as a rubber duck that’d be good too ![]()
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
Hi everyone,
I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding.
I sta...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
Documentation
While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
New
Other Trending Topics
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Hi everyone!
The first release candidate for the Expert language server project is now available!
We’ve published a press release detai...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #elixirconf-eu
- #metaprogramming
- #hex











Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
christhekeele
It’s worth remembering that the return value of a macro just needs to be a valid AST data structure to be inserted somewhere else, which can be composed however you like, including raw literals! It’s easy to forget sometimes, because
quoteis so powerful, and I often fall into this block-based thinking where the final expression of adefmacrohas to be a top-levelquoteblock.However, an equally valid return value is a list of AST snippets, which will get interpreted as a series of sequential expressions. So you can just tuck the
quoteinside thefor, and return a list of quoted expressions:sodapopcan
Hey Chris! So had I tried something like this but of course I didn’t give the whole picture in my post. I’m iterating over protocol impls, so they need to be retrieved in a
quoteblock in order for it to be consolidated (as far as I can tell, at least). However, you’ve definitely given me a good hint here. I assume I’m going to have to iterate over ast and inject it. Something like:Something like that? In any event, you’ve given me a good hint for a way forward. Thanks!!
I’ll report back if I get this working.
christhekeele
Ah yeah, that’ll be tricky, based on when protocols get consolidated during the compilation process. Definitely interested to learn if you solve it!
sodapopcan
I am quickly realizing this
I think I’m going to explore other options.
EDIT: Ya, re-reading the docs they are consolidated after everything else is compiled, so what I want to do is not possible. I have a runtime version of it working so I will probably just stick with that for now.
Thanks again for your help!
kip
When you have nested quote blocks, you need to give Elixir a hint as to what an
unuquoteapplies to. You do this by passingquote unquote: falseto the outer block. The following compiles, but I haven’t tested if its actually what you want:sodapopcan
Whoa! With a slight tweak that worked! Thanks so much, Kip! Though now I’m puzzled as to how it works with protocol impls, though the docs do qualify that they are usually consolidated after the app is compiled. I’m guessing if there is a compile-time dependency on it that will make it happen earlier. That is maybe supported by this but I haven’t taken the time to grok it yet.
Thanks again!
christhekeele
I suspect it’s just a race condition in the compiler, and beating it is a matter of project size.
Not to
XYproblem you, but why are you trying to metaprogram generated functions based on structs implementing protocols? I ask for three reasons,Yof your problemsodapopcan
This is very unsurprising to hear. I had an inkling that I was ignoring for now while I reveled in this working. I feels incredibly janky, of course!
I don’t mind being XY’d, I do it to people on this site but ya, I guess they sometimes haven’t appreciated it
I’m just playing around building a little test factory lib. I’m looking for a way to define factory definitions across multiple files with little ceremony, so protocols came to mind. Of course, I don’t want to do
And now it works, but only in my very small tests, of course! I just got fixated on making that particular solution work but there’s probably a simpler way? I’m happy to hear of your crimes!
build(MyApp.Context.Entity), I want to dobuild(:entity), so then I got this idea of writing a macro that stores the key in the protocol implementations then do roughly what I shared up thereI know I can also just a process or ETS for this, the game was more to see if I could avoid it.
christhekeele
I missed this response til now, happy to oblige!
There’s no real way to do this at compile time as a library AFAICT, since to be race-condition-proof, you have to assume your library modules must always compile before the application code that would register its own modules under these aliases with your library module—your modules are not “open” for modification in the Ruby sense.
You have more options if you are willing to write macros in your namespace (ex
Factory) that help application code define its own module within its own namespace (exApp.Factory), however.Based off of my uses tracker, you could have an API like:
Of course, at this point you may as well use compile-time
Config. However, if you’d rather the:asconfiguration be defined adjacent to the definition of the struct modules themselves, like so:…then you have to get a little fancier, but it can definitely be done. This approach is overkill, but convenient if you’re deadset on this approach anyways and
Factory.Generatorhas other work it wants to do in the struct module already.sodapopcan
I thought about something along the lines of the builder you suggested, but my goal was to either make it dead-simple or not bother at all. I think I’m going to not bother at all
I’m mostly just experimenting on making some small QOL improvements on Ecto’s test factories, which I like very much and are all I’ve ever used in my personal projects. I’ll take a closer look at your uses tracker and maybe get inspired!
Thanks for all the info, it’s much appreciated!