wrachwal
Macro to generate function with body containing an arg and var bound in outer scope?
Hi,
I am trying to define a macro with 3 arguments (name, var, body) which is to generate function name/1
a) with argument var, possibly arbitrary complex pattern-matching expression
b) besides var, other variables bound in the outer scope can be used in the body
Example:
defmodule Mac3Use do
import Mac3
for {g, v} <- [g1: 1, g2: 2] do
gen String.to_atom("#{g}_x"), a, do: {:ok, 1, a}
gen String.to_atom("#{g}_y"), %{y: y} = a, do: {:ok, y, a}
# gen String.to_atom("#{g}_z"), a, do: {:ok, v, a} # undefined function v/0
end
end
Below the implementation satisfying a). With slight modification it can handle guards. Unfortunately it doesn’t handle b) - I get “undefined function v/0” CompileError.
defmodule Mac3 do
defmacro gen(name, var \\ quote(do: _), do: block) do
var = Macro.escape(var)
block = Macro.escape(block)
quote bind_quoted: [name: name, var: var, block: block] do
def unquote(name)(unquote(var)) do
unquote(block)
end
end
end
end
I was trying to modify this code without luck. Inspect of binding() inside quote shows g and v variables from the example with proper values bound, but unquote(block) doesn’t want to consume v.
Just a moment ago I checked that
@v v
gen String.to_atom("#{g}_z"), a, do: {:ok, @v, a}
works, but it’s rather a hack.
As you could notice, the macro resembles ExUnit.Case.test/3, and its implementation is simplified version of that from ExUnit.
Is there an easy solution to this?
Thank you,
Waldemar.
Marked As Solved
NobbZ
I still think unquote: true is the option you actually want… You’ll have to write unquote(v) then, but at least it should work. And as far as I remember ExUnit, you have to unquote “external” names there as well…
Also Liked
NobbZ
As you can see in the code, they are unquoteing contents (your body) first before escapeing it. Also they escape using the unquote: true option.
Perhaps you should evaluate those ways?
https://github.com/elixir-lang/elixir/blob/v1.5.2/lib/ex_unit/lib/ex_unit/case.ex#L258-L280
jfeng
I think changing
gen String.to_atom("#{g}_z"), a, do: {:ok, v, a} # undefined function v/0
to
gen String.to_atom("#{g}_z"), a, do: {:ok, unquote(v), a} # should work fine this way
should work. As far as I can tell, you want to use a value of v that’s only available at compile time in the module, but as you have it, v and a are just regular variables declared in the body of a function.
Last Post!
OvermindDL1
Do note, that only works if v is valid as an AST (which in this specific case a raw number is), otherwise you want unquote(Macro.escape(v)) instead, which will always safely put it in the AST (well unless it is a PID or Ref or so). ![]()
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









