wrachwal
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.
Trending in Questions
Other Trending Topics
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
- #elixirconf-us
- #ai
- #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)
OvermindDL1
The
vin the:ok3-tuple is scoped inside thegenmacro, you need to unquote it:And that ‘should’ work I think?
wrachwal
unquote(v)results in “unquote called outside quote”OvermindDL1
Ah it’s not working like it does in a
def’s body, probably some special casing there… >.<I wonder if it is because you are
Macro.escapeing the block, why are you doing that (and why escaping the var too)?wrachwal
Good question. Some sort of hacking. Sure, all macro arguments are already AST(s). Nevertheless without escaping e.g. var, it will choke on
bind_quoted. Comment out escaping var and you’ll get “undefined function a/0” for the first function being generated that worked earlier.During experiments I realized that
bind_quotedis somehow opposite to escaping (hence the name). In this particular use case this second escape (first is implicit and forms macro arguments) seems to prevent “finding” a value inbind_quotedconstruct for what is to be a function argument - the variable, for which there is no value to bind, right?All this hacking is not mine - see
ExUnit.Case.test/3for comparison.https://github.com/elixir-lang/elixir/blob/v1.5.2/lib/ex_unit/lib/ex_unit/case.ex#L258-L280
OvermindDL1
Well the main thing I’m curious about is what is the point of this macro, wouldn’t this be easier?
I think this is the XY problem, I’m not sure what is trying to be accomplished here. ^.^;
wrachwal
Let’s say I want compile time bindings (variables, not attributes) to be visible in the body of something like
ExUnit.Case.test/3.NobbZ
As you can see in the code, they are
unquoteingcontents(yourbody) first beforeescapeing it. Also theyescapeusing theunquote: trueoption.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
wrachwal
I tried this earlier with no desirable effect. I think the whole point behind this quote/unquote/escape is to assure that testcases return
:ok(it’s mentioned in the comment above the macro’s head).To unhide the context a bit - I’d want such a feature in macros I am building around
common_test; In common_test you can put a set of testcases in one group, and to run such the set with different input configurations, you need to place that group into several intermediate groups and provide specific configuration for such intermediates.Definining intermediate groups can be tedious, so looping with
for/1seems to be the best fit for the job. If I don’t find a way how to do that, the whole work won’t be lost - variable bindings from outer scope would be nice addition, though.Nevertheless I think the problem seems interesting in general.
NobbZ
I still think
unquote: trueis the option you actually want… You’ll have to writeunquote(v)then, but at least it should work. And as far as I remember ExUnit, you have to unquote “external” names there as well…wrachwal
You are probably right… Quick check:
yields
I will play a little more tomorrow (now is close to midnight).
Thank you very much,
Waldemar.