Macro that is just a string at runtime

I need a macro that is just a string at runtime.

defmodule GoodMacro do
@wow "wow!_"

  defmacro boop(val) do
    "boop_" <> @wow <> val
  end
end
defmodule Cat do
require GoodMacro

  def meow() do
    GoodMacro.boop("cat")
  end
end

Once compiled, that will be just a string, right? I want it to be as if Kernel.<> was never called. Is my implementation correct?

...
 def meow() do
    "boop_wow!_cat"
 end
...

Yes, but why?

I knew someone would ask that :slight_smile:

I have static keys that will be evaluated thousands of times per second. I figured it might be more efficient this way. Maybe not?

Depending on your requirements, you could use Atoms instead.

Right if they’re truly static just use atoms.

I’d rather not. The sender sends the data in this format:

[{"Key1", "value1"},
{"Some-Key2", "value2"},
{"some_key_3", "value3"},
...]

It expects the same format in response. Isn’t it inefficient to convert from a string to an atom back into a string? My application and the application I’m talking to get very chatty.

A cleaner approach would be the following:

defmodule GoodMacro do
  @wow "wow!_"

  defmacro boop(val) do
    result = "boop_" <> @wow <> val
    quote do
      unquote(result)
    end
  end
end
defmodule Cat do
  require GoodMacro

  def meow() do
    GoodMacro.boop(cat)
  end
end

The code now no longer relies on the fact that a binary happens to be represented by just a binary in the Elixir AST, meaning that inside GoodMacro.boop/1, result could be altered to other things, like result = {1,@wow, val} or result= %{a: 1, foo: @wow, val: val} etc. :slight_smile:

Indeed, performing things once during compilation that would otherwise happen very frequently during runtime is a great opportunity for optimization. But of course, do not do so prematurely :stuck_out_tongue_winking_eye:.

3 Likes

GoodMacro.boop/1 could be altered later on regardless of how it was initially implemented internally. I actually think the original implementation was cleaner.