janp
Keyword.put in macro results in AST instead of keyword list
I am currently stuck in implementing a macro
Here’s the code I currently have (without unnecessary code):
defmacro request(opts \\ [], do: expr) do
quote
typedstruct unquote(opts) do
unquote(expr)
end
end
end
That macro works fine, but I would like to add some options that cannot be overwritten, so I tried this:
defmacro request(opts \\ [], do: expr) do
quote
typedstruct Keyword.put(unquote(opts), :module, Req) do
unquote(expr)
end
end
end
Sadly, this fails with a compilation error:
** (FunctionClauseError) no function clause matching in Access.get/3
The following arguments were given to Access.get/3:
# 1
{{:., [line: 6], [{:__aliases__, [line: 6, counter: {ChangeUser, 8}, alias: false], [:Keyword]}, :put]}, [line: 6], [[], :module, {:__aliases__, [line: 6, counter: {ChangeUser, 8}, alias: false], [:Req]}]}
# 2
:enforce
# 3
nil
Attempted function clauses (showing 6 out of 6):
def get(%module{} = container, key, default)
def get(map, key, default) when is_map(map)
def get(list, key, default) when is_list(list) and is_atom(key)
def get(list, key, _default) when is_list(list) and is_integer(key)
def get(list, key, _default) when is_list(list)
def get(nil, _key, default)
That error occurs, because an AST is passed to the typedstruct-macro instead of a keyword list and typed_struct then tries to call opts[:enforce] (tuples do not implement the Access-behaviour).
Is there anyone who can help me figure out what is happening and how to solve this issue? ![]()
Marked As Solved
sodapopcan
Ah yes, that changes things!
So first thing, the way you’re doing use TypedStruct isn’t going to work. You actually want ChangeUser (or whatever module is useing Operation) to be using it. Perhaps you’ve left out some code, but basically you want something like this:
defmodule Operation do
defmacro __using__(_) do
quote do
use TypedStruct
import Operation
end
end
defmacro request(opts \\ [], do: expr) do
opts = Keyword.put(opts, :module, Module.concat([__CALLER__.module, Req]))
quote do
typedstruct unquote(opts) do
unquote(expr)
end
end
end
end
Also Liked
sodapopcan
Move it outside of the quote:
defmacro request(opts \\ [], do: expr) do
opts = Keyword.put(opts, :module, Req)
quote do
typedstruct unquote(opts) do
unquote(expr)
end
end
end
Otherwise, as you pointed out, the code you end up with will literally be the same as typing out:
typedstruct Keyword.put([some: :user, provided: :opts], :module, Req) do
# unquoted_expr
end
The reason you don’t have to do anything special with opts in the macro body is that lists are one of the constructs that return themselves when quoted.
EDIT: Just re-reading my answer after getting pinged about it:
For good measure you should call Macro.escape on the value you put in the keyword list:
opts = Keyword.put(opts, :module, Macro.escape(Req))
It won’t make any difference in this case (Macro.escape(Req) #=> Req) but if you put other values (like maps, for example) as value, you’ll need to do this so that you get the proper ast representation to be later unquoted.
janp
Great
It works!
A few minutes ago, I found some code using __CALLER__, but I was unsure about it. Now I know it better.
Thank you!
sodapopcan
No prob!
You often don’t need to use __CALLER__ but in this case typedstruct is a macro itself, so of course its arguments get quoted so you need to grab the caller’s module name outside the quote block. But most of the time you can just use __MODULE__ inside the quote block:
defmodule Bar do
defmarco __using__(_) do
quote do
dbg(__MODULE__)
end
end
end
defmodule Foo do
use Bar
end
dbg here will be Foo.
Last Post!
sodapopcan
No prob!
You often don’t need to use __CALLER__ but in this case typedstruct is a macro itself, so of course its arguments get quoted so you need to grab the caller’s module name outside the quote block. But most of the time you can just use __MODULE__ inside the quote block:
defmodule Bar do
defmarco __using__(_) do
quote do
dbg(__MODULE__)
end
end
end
defmodule Foo do
use Bar
end
dbg here will be Foo.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #hex









