How to do a macro that return another macro?

Hello ^^, I’m creating a API with Absinthe, I’m having many arguments that are being duplicated again and again, so I would like to implement a macro that calls that argument that they have all in common, for example change this:

    field :person, :person do
      arg(:slug, non_null(:string))

      arg(
        :language,
        :query_language,
        description: "A large description",
        default_value: :en
      )
    end

    field :company, :company do
      arg(:slug, non_null(:string))

      arg(
        :language,
        :query_language,
        description: "A large description",
        default_value: :en
      )
    end

to something like this:

    field :person, :person do
      arg(:slug, non_null(:string))

      get_language_argument()
    end

    field :company, :company do
      arg(:slug, non_null(:string))

      get_language_argument()
    end

So the get_language_argument(), return the arg macro, is that possible?

Greetings ^^/

Have you tried it?

defmacro get_language_argument do
  arg(
    :language,
    :query_language,
    description: "A large description",
    default_value: :en
  )
end

Should just work… But why do you need a macro? Why not just a function for get_language_argument?

Yeah I already tried it, but I get a compilation error

** (Absinthe.Schema.Notation.Error) Invalid schema notation: `arg` must only be used within `directive`, `field`
    lib/absinthe/schema/notation.ex:1463: Absinthe.Schema.Notation.do_recordable!/4
    expanding macro: Absinthe.Schema.Notation.arg/3
    lib/okami/schema.ex:176: Okami.Schema.get_language_argument/0
    (elixir) lib/kernel/parallel_compiler.ex:198: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6

Oh well yeah, there is no need for a macro if a function will work ^^’

What you want is to return AST that contains the arg macro you’re trying to inject.

defmacro get_language_argument do
  quote do
    arg(
      :language,
      :query_language,
      description: "A large description",
      default_value: :en
    )
  end
end

As far as the implied use case goes, unless it’s very common to request different fields in different languages I would consider setting a more global language flag in the context and just using that, instead of specifying it on every field.

1 Like

Oh… Stupid me… forgot the quoteing… That happens when one tries to answer from a computer that has no elixir availbe and is in a hurry…

1 Like