wrachwal

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

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

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

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

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). :slight_smile:

Where Next?

Popular in Questions Top

vegabook
I’m brand new to Phoenix and I have stripped one of the demo applications to the bone. I just want to get an svg up on the screen. Here i...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New

Other popular topics Top

electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
vertexbuffer
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
greenz1
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
axelson
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!) This post collects co...
239 49084 226
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 44139 214
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID&lt;0.412.0&gt; terminating ** (Postgrex.Error) FATAL...
New

We're in Beta

About us Mission Statement