Pass Module attribute as an argument to Macros












0















It's common to have constants as Module Attributes in Elixir. I've tried to pass module attributes as arguments to different macros from different libraries (that usually define a new module):





defmodule X do
@data_types [:x, :y, :z]
@another_constant "some-constant-value"

defenum DataType, :type, @data_types
end


Here's another example of passing module attributes as an argument to a Macro





But almost always get an error along the same lines:



** (Protocol.UndefinedError) protocol Enumerable not implemented for {:@, [line: 26], [{:types, [line: 26], nil}]}


So I usually end up repeating the values:



defmodule X do
@data_types [:x, :y, :z]
@another_constant "some-constant-value"

defenum DataType, :type, [:x, :y, :z]
end


I know repeating them most of the time isn't usually a big deal, but I would really like to know how would I be able to pass the value of a module attribute to a macro.



This is especially apparent in macros that define new modules (like Amnesia and EctoEnum).





So far I've tried a bunch of things, including:




  • Expanding the value using the Macro module

  • Evaluating the value using Code module

  • Fetching the value using Module.get_attribute/2

  • Trying different variations of quote/unquote calls


But nothing has worked. I have a feeling the macro needs to be written in a way that it can read them. If so, how should the macro be written for it to work?










share|improve this question





























    0















    It's common to have constants as Module Attributes in Elixir. I've tried to pass module attributes as arguments to different macros from different libraries (that usually define a new module):





    defmodule X do
    @data_types [:x, :y, :z]
    @another_constant "some-constant-value"

    defenum DataType, :type, @data_types
    end


    Here's another example of passing module attributes as an argument to a Macro





    But almost always get an error along the same lines:



    ** (Protocol.UndefinedError) protocol Enumerable not implemented for {:@, [line: 26], [{:types, [line: 26], nil}]}


    So I usually end up repeating the values:



    defmodule X do
    @data_types [:x, :y, :z]
    @another_constant "some-constant-value"

    defenum DataType, :type, [:x, :y, :z]
    end


    I know repeating them most of the time isn't usually a big deal, but I would really like to know how would I be able to pass the value of a module attribute to a macro.



    This is especially apparent in macros that define new modules (like Amnesia and EctoEnum).





    So far I've tried a bunch of things, including:




    • Expanding the value using the Macro module

    • Evaluating the value using Code module

    • Fetching the value using Module.get_attribute/2

    • Trying different variations of quote/unquote calls


    But nothing has worked. I have a feeling the macro needs to be written in a way that it can read them. If so, how should the macro be written for it to work?










    share|improve this question



























      0












      0








      0








      It's common to have constants as Module Attributes in Elixir. I've tried to pass module attributes as arguments to different macros from different libraries (that usually define a new module):





      defmodule X do
      @data_types [:x, :y, :z]
      @another_constant "some-constant-value"

      defenum DataType, :type, @data_types
      end


      Here's another example of passing module attributes as an argument to a Macro





      But almost always get an error along the same lines:



      ** (Protocol.UndefinedError) protocol Enumerable not implemented for {:@, [line: 26], [{:types, [line: 26], nil}]}


      So I usually end up repeating the values:



      defmodule X do
      @data_types [:x, :y, :z]
      @another_constant "some-constant-value"

      defenum DataType, :type, [:x, :y, :z]
      end


      I know repeating them most of the time isn't usually a big deal, but I would really like to know how would I be able to pass the value of a module attribute to a macro.



      This is especially apparent in macros that define new modules (like Amnesia and EctoEnum).





      So far I've tried a bunch of things, including:




      • Expanding the value using the Macro module

      • Evaluating the value using Code module

      • Fetching the value using Module.get_attribute/2

      • Trying different variations of quote/unquote calls


      But nothing has worked. I have a feeling the macro needs to be written in a way that it can read them. If so, how should the macro be written for it to work?










      share|improve this question
















      It's common to have constants as Module Attributes in Elixir. I've tried to pass module attributes as arguments to different macros from different libraries (that usually define a new module):





      defmodule X do
      @data_types [:x, :y, :z]
      @another_constant "some-constant-value"

      defenum DataType, :type, @data_types
      end


      Here's another example of passing module attributes as an argument to a Macro





      But almost always get an error along the same lines:



      ** (Protocol.UndefinedError) protocol Enumerable not implemented for {:@, [line: 26], [{:types, [line: 26], nil}]}


      So I usually end up repeating the values:



      defmodule X do
      @data_types [:x, :y, :z]
      @another_constant "some-constant-value"

      defenum DataType, :type, [:x, :y, :z]
      end


      I know repeating them most of the time isn't usually a big deal, but I would really like to know how would I be able to pass the value of a module attribute to a macro.



      This is especially apparent in macros that define new modules (like Amnesia and EctoEnum).





      So far I've tried a bunch of things, including:




      • Expanding the value using the Macro module

      • Evaluating the value using Code module

      • Fetching the value using Module.get_attribute/2

      • Trying different variations of quote/unquote calls


      But nothing has worked. I have a feeling the macro needs to be written in a way that it can read them. If so, how should the macro be written for it to work?







      elixir






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 2 at 3:02







      Sheharyar

















      asked Jan 2 at 0:42









      SheharyarSheharyar

      46k12110164




      46k12110164
























          2 Answers
          2






          active

          oldest

          votes


















          1














          Unfortunately, the only way to fix the issue with passing arbitrary quoted expressions to external libraries would be to provide pull requests fixing issues in the libraries.



          Consider the following example





          defmodule Macros do
          defmacro good(param) do
          IO.inspect(param, label: "👍 Passed")
          expanded = Macro.expand(param, __CALLER__)
          IO.inspect(expanded, label: "👍 Expanded")
          end

          defmacro bad(param) do
          IO.inspect(param, label: "👎 Not Expanded")
          end
          end

          defmodule Test do
          import Macros
          @data_types [:x, :y, :z]

          def test do
          good(@data_types)
          bad(@data_types)
          end
          end


          The declaration of Test prints:



          👍 Passed: {:@, [line: 28], [{:data_types, [line: 28], nil}]}
          👍 Expanded: [:x, :y, :z]
          👎 Not Expanded: {:@, [line: 29], [{:data_types, [line: 29], nil}]}


          If the 3rd-party library does not call Macro.expand/2 on the argument, the quoted expressions won’t be expanded. Below is the excerpt from the documentation:




          The following contents are expanded:



          • Macros (local or remote)

          • Aliases are expanded (if possible) and return atoms

          • Compilation environment macros (__CALLER__/0, __DIR__/0, __ENV__/0 and __MODULE__/0)

          • Module attributes reader (@foo)




          That said, to have the ability to accept module attributes or macro calls like sigils, the 3rd party library macros must call Macro.expand on arguments. You cannot fix this issue from your client code.






          share|improve this answer



















          • 1





            This seems like what I was looking for. I have no problem sending in PRs to different packages if this can get it to work. :)

            – Sheharyar
            Jan 2 at 6:28






          • 1





            Sidenote: defenum DataType, :type, ~w[x y z]a won’t work either without Macro.expand, that’s why I always use sigils in tests when working with macros.

            – Aleksei Matiushkin
            Jan 2 at 6:30






          • 1





            That's interesting, didn't know that either. I'll start by updating my own packages and send PRs to others in the next few weeks. Thanks!

            – Sheharyar
            Jan 2 at 6:32













          • Always welcome!

            – Aleksei Matiushkin
            Jan 2 at 6:32











          • What info does bad/1 provide that is not provided by IO.inspect(param, label: "👍 Passed") inside good/1?

            – 7stud
            Jan 4 at 1:31



















          0















          But almost always get an error...




          I'm able to access a module attribute inside a macro in this example:



          defmodule My do
          @data_types [:x, :y, :z]

          defmacro go() do
          quote do
          def types() do
          unquote(@data_types)
          end
          end
          end
          end

          defmodule Test do
          require My
          My.go()

          def start do
          types()
          end
          end


          In iex:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

          Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

          iex(1)> Test.start()
          [:x, :y, :z]

          iex(2)>


          Response to comment:



          Here's an example that passes a module attribute as an argument to a macro function call:



          defmodule My do

          defmacro go(arg) do
          quote do
          def show do
          Enum.each(unquote(arg), fn x -> IO.inspect x end)
          end
          end
          end

          end

          defmodule Test do
          @data_types [:x, :y, :z]

          require My
          My.go(@data_types)

          def start do
          show()
          end

          end


          In iex:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
          Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

          iex(1)> Test.start()
          :x
          :y
          :z
          :ok

          iex(2)>


          If instead, you try this:



          defmodule My do
          defmacro go(arg) do
          Enum.each(arg, fn x -> IO.inspect x end)
          end

          end

          defmodule Test do
          require My
          @data_types [:x, :y, :z]
          My.go(@data_types)

          def start do
          show()
          end
          end


          then in iex you'll get an error:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

          ** (Protocol.UndefinedError) protocol Enumerable not implemented for {:@, [line: 11], [{:data_types, [line: 11], nil}]}
          (elixir) lib/enum.ex:1: Enumerable.impl_for!/1
          (elixir) lib/enum.ex:141: Enumerable.reduce/3
          (elixir) lib/enum.ex:1919: Enum.each/2
          expanding macro: My.go/1
          my.exs:11: Test (module)


          That error occurs because you are trying to enumerate an ast:



          {:@, [line: 11], [{:data_types, [line: 11], nil}]}


          which is not a list (or any other enumerable--it's obviously a tuple!). Remember that arguments to macros are ast's.



          The reason this works:



            defmacro go(arg) do

          quote do
          def show do
          Enum.each(unquote(arg), fn x -> IO.inspect x end)
          end
          end

          end


          is because quote() creates an ast, and unquote(arg) injects some other ast into the middle of the ast. The macro call My.go(@data_types) executes at compile time, and elixir replaces the macro call with the ast returned by My.go(), which is a function definition named show().






          share|improve this answer


























          • I want to pass module attribute values as an argument to the macro, not access it directly (which I know is easy)

            – Sheharyar
            Jan 2 at 1:52











          • Updated the question title, though I thought the examples in the question already made that pretty clear :)

            – Sheharyar
            Jan 2 at 1:52













          • @Sheharyar, See additional example at the bottom of my answer.

            – 7stud
            Jan 2 at 2:22











          • Your example works because you're defining a method in the same module. It still doesn't solve the problem because quote(do: unquote(@attr)) is still @attr and not it's value. Most library macros generate a new module at compile-time which will not be able to read the value of the module attr passed as arg with this.

            – Sheharyar
            Jan 2 at 3:01











          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54000082%2fpass-module-attribute-as-an-argument-to-macros%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          2 Answers
          2






          active

          oldest

          votes








          2 Answers
          2






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1














          Unfortunately, the only way to fix the issue with passing arbitrary quoted expressions to external libraries would be to provide pull requests fixing issues in the libraries.



          Consider the following example





          defmodule Macros do
          defmacro good(param) do
          IO.inspect(param, label: "👍 Passed")
          expanded = Macro.expand(param, __CALLER__)
          IO.inspect(expanded, label: "👍 Expanded")
          end

          defmacro bad(param) do
          IO.inspect(param, label: "👎 Not Expanded")
          end
          end

          defmodule Test do
          import Macros
          @data_types [:x, :y, :z]

          def test do
          good(@data_types)
          bad(@data_types)
          end
          end


          The declaration of Test prints:



          👍 Passed: {:@, [line: 28], [{:data_types, [line: 28], nil}]}
          👍 Expanded: [:x, :y, :z]
          👎 Not Expanded: {:@, [line: 29], [{:data_types, [line: 29], nil}]}


          If the 3rd-party library does not call Macro.expand/2 on the argument, the quoted expressions won’t be expanded. Below is the excerpt from the documentation:




          The following contents are expanded:



          • Macros (local or remote)

          • Aliases are expanded (if possible) and return atoms

          • Compilation environment macros (__CALLER__/0, __DIR__/0, __ENV__/0 and __MODULE__/0)

          • Module attributes reader (@foo)




          That said, to have the ability to accept module attributes or macro calls like sigils, the 3rd party library macros must call Macro.expand on arguments. You cannot fix this issue from your client code.






          share|improve this answer



















          • 1





            This seems like what I was looking for. I have no problem sending in PRs to different packages if this can get it to work. :)

            – Sheharyar
            Jan 2 at 6:28






          • 1





            Sidenote: defenum DataType, :type, ~w[x y z]a won’t work either without Macro.expand, that’s why I always use sigils in tests when working with macros.

            – Aleksei Matiushkin
            Jan 2 at 6:30






          • 1





            That's interesting, didn't know that either. I'll start by updating my own packages and send PRs to others in the next few weeks. Thanks!

            – Sheharyar
            Jan 2 at 6:32













          • Always welcome!

            – Aleksei Matiushkin
            Jan 2 at 6:32











          • What info does bad/1 provide that is not provided by IO.inspect(param, label: "👍 Passed") inside good/1?

            – 7stud
            Jan 4 at 1:31
















          1














          Unfortunately, the only way to fix the issue with passing arbitrary quoted expressions to external libraries would be to provide pull requests fixing issues in the libraries.



          Consider the following example





          defmodule Macros do
          defmacro good(param) do
          IO.inspect(param, label: "👍 Passed")
          expanded = Macro.expand(param, __CALLER__)
          IO.inspect(expanded, label: "👍 Expanded")
          end

          defmacro bad(param) do
          IO.inspect(param, label: "👎 Not Expanded")
          end
          end

          defmodule Test do
          import Macros
          @data_types [:x, :y, :z]

          def test do
          good(@data_types)
          bad(@data_types)
          end
          end


          The declaration of Test prints:



          👍 Passed: {:@, [line: 28], [{:data_types, [line: 28], nil}]}
          👍 Expanded: [:x, :y, :z]
          👎 Not Expanded: {:@, [line: 29], [{:data_types, [line: 29], nil}]}


          If the 3rd-party library does not call Macro.expand/2 on the argument, the quoted expressions won’t be expanded. Below is the excerpt from the documentation:




          The following contents are expanded:



          • Macros (local or remote)

          • Aliases are expanded (if possible) and return atoms

          • Compilation environment macros (__CALLER__/0, __DIR__/0, __ENV__/0 and __MODULE__/0)

          • Module attributes reader (@foo)




          That said, to have the ability to accept module attributes or macro calls like sigils, the 3rd party library macros must call Macro.expand on arguments. You cannot fix this issue from your client code.






          share|improve this answer



















          • 1





            This seems like what I was looking for. I have no problem sending in PRs to different packages if this can get it to work. :)

            – Sheharyar
            Jan 2 at 6:28






          • 1





            Sidenote: defenum DataType, :type, ~w[x y z]a won’t work either without Macro.expand, that’s why I always use sigils in tests when working with macros.

            – Aleksei Matiushkin
            Jan 2 at 6:30






          • 1





            That's interesting, didn't know that either. I'll start by updating my own packages and send PRs to others in the next few weeks. Thanks!

            – Sheharyar
            Jan 2 at 6:32













          • Always welcome!

            – Aleksei Matiushkin
            Jan 2 at 6:32











          • What info does bad/1 provide that is not provided by IO.inspect(param, label: "👍 Passed") inside good/1?

            – 7stud
            Jan 4 at 1:31














          1












          1








          1







          Unfortunately, the only way to fix the issue with passing arbitrary quoted expressions to external libraries would be to provide pull requests fixing issues in the libraries.



          Consider the following example





          defmodule Macros do
          defmacro good(param) do
          IO.inspect(param, label: "👍 Passed")
          expanded = Macro.expand(param, __CALLER__)
          IO.inspect(expanded, label: "👍 Expanded")
          end

          defmacro bad(param) do
          IO.inspect(param, label: "👎 Not Expanded")
          end
          end

          defmodule Test do
          import Macros
          @data_types [:x, :y, :z]

          def test do
          good(@data_types)
          bad(@data_types)
          end
          end


          The declaration of Test prints:



          👍 Passed: {:@, [line: 28], [{:data_types, [line: 28], nil}]}
          👍 Expanded: [:x, :y, :z]
          👎 Not Expanded: {:@, [line: 29], [{:data_types, [line: 29], nil}]}


          If the 3rd-party library does not call Macro.expand/2 on the argument, the quoted expressions won’t be expanded. Below is the excerpt from the documentation:




          The following contents are expanded:



          • Macros (local or remote)

          • Aliases are expanded (if possible) and return atoms

          • Compilation environment macros (__CALLER__/0, __DIR__/0, __ENV__/0 and __MODULE__/0)

          • Module attributes reader (@foo)




          That said, to have the ability to accept module attributes or macro calls like sigils, the 3rd party library macros must call Macro.expand on arguments. You cannot fix this issue from your client code.






          share|improve this answer













          Unfortunately, the only way to fix the issue with passing arbitrary quoted expressions to external libraries would be to provide pull requests fixing issues in the libraries.



          Consider the following example





          defmodule Macros do
          defmacro good(param) do
          IO.inspect(param, label: "👍 Passed")
          expanded = Macro.expand(param, __CALLER__)
          IO.inspect(expanded, label: "👍 Expanded")
          end

          defmacro bad(param) do
          IO.inspect(param, label: "👎 Not Expanded")
          end
          end

          defmodule Test do
          import Macros
          @data_types [:x, :y, :z]

          def test do
          good(@data_types)
          bad(@data_types)
          end
          end


          The declaration of Test prints:



          👍 Passed: {:@, [line: 28], [{:data_types, [line: 28], nil}]}
          👍 Expanded: [:x, :y, :z]
          👎 Not Expanded: {:@, [line: 29], [{:data_types, [line: 29], nil}]}


          If the 3rd-party library does not call Macro.expand/2 on the argument, the quoted expressions won’t be expanded. Below is the excerpt from the documentation:




          The following contents are expanded:



          • Macros (local or remote)

          • Aliases are expanded (if possible) and return atoms

          • Compilation environment macros (__CALLER__/0, __DIR__/0, __ENV__/0 and __MODULE__/0)

          • Module attributes reader (@foo)




          That said, to have the ability to accept module attributes or macro calls like sigils, the 3rd party library macros must call Macro.expand on arguments. You cannot fix this issue from your client code.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Jan 2 at 6:03









          Aleksei MatiushkinAleksei Matiushkin

          83.5k95694




          83.5k95694








          • 1





            This seems like what I was looking for. I have no problem sending in PRs to different packages if this can get it to work. :)

            – Sheharyar
            Jan 2 at 6:28






          • 1





            Sidenote: defenum DataType, :type, ~w[x y z]a won’t work either without Macro.expand, that’s why I always use sigils in tests when working with macros.

            – Aleksei Matiushkin
            Jan 2 at 6:30






          • 1





            That's interesting, didn't know that either. I'll start by updating my own packages and send PRs to others in the next few weeks. Thanks!

            – Sheharyar
            Jan 2 at 6:32













          • Always welcome!

            – Aleksei Matiushkin
            Jan 2 at 6:32











          • What info does bad/1 provide that is not provided by IO.inspect(param, label: "👍 Passed") inside good/1?

            – 7stud
            Jan 4 at 1:31














          • 1





            This seems like what I was looking for. I have no problem sending in PRs to different packages if this can get it to work. :)

            – Sheharyar
            Jan 2 at 6:28






          • 1





            Sidenote: defenum DataType, :type, ~w[x y z]a won’t work either without Macro.expand, that’s why I always use sigils in tests when working with macros.

            – Aleksei Matiushkin
            Jan 2 at 6:30






          • 1





            That's interesting, didn't know that either. I'll start by updating my own packages and send PRs to others in the next few weeks. Thanks!

            – Sheharyar
            Jan 2 at 6:32













          • Always welcome!

            – Aleksei Matiushkin
            Jan 2 at 6:32











          • What info does bad/1 provide that is not provided by IO.inspect(param, label: "👍 Passed") inside good/1?

            – 7stud
            Jan 4 at 1:31








          1




          1





          This seems like what I was looking for. I have no problem sending in PRs to different packages if this can get it to work. :)

          – Sheharyar
          Jan 2 at 6:28





          This seems like what I was looking for. I have no problem sending in PRs to different packages if this can get it to work. :)

          – Sheharyar
          Jan 2 at 6:28




          1




          1





          Sidenote: defenum DataType, :type, ~w[x y z]a won’t work either without Macro.expand, that’s why I always use sigils in tests when working with macros.

          – Aleksei Matiushkin
          Jan 2 at 6:30





          Sidenote: defenum DataType, :type, ~w[x y z]a won’t work either without Macro.expand, that’s why I always use sigils in tests when working with macros.

          – Aleksei Matiushkin
          Jan 2 at 6:30




          1




          1





          That's interesting, didn't know that either. I'll start by updating my own packages and send PRs to others in the next few weeks. Thanks!

          – Sheharyar
          Jan 2 at 6:32







          That's interesting, didn't know that either. I'll start by updating my own packages and send PRs to others in the next few weeks. Thanks!

          – Sheharyar
          Jan 2 at 6:32















          Always welcome!

          – Aleksei Matiushkin
          Jan 2 at 6:32





          Always welcome!

          – Aleksei Matiushkin
          Jan 2 at 6:32













          What info does bad/1 provide that is not provided by IO.inspect(param, label: "👍 Passed") inside good/1?

          – 7stud
          Jan 4 at 1:31





          What info does bad/1 provide that is not provided by IO.inspect(param, label: "👍 Passed") inside good/1?

          – 7stud
          Jan 4 at 1:31













          0















          But almost always get an error...




          I'm able to access a module attribute inside a macro in this example:



          defmodule My do
          @data_types [:x, :y, :z]

          defmacro go() do
          quote do
          def types() do
          unquote(@data_types)
          end
          end
          end
          end

          defmodule Test do
          require My
          My.go()

          def start do
          types()
          end
          end


          In iex:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

          Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

          iex(1)> Test.start()
          [:x, :y, :z]

          iex(2)>


          Response to comment:



          Here's an example that passes a module attribute as an argument to a macro function call:



          defmodule My do

          defmacro go(arg) do
          quote do
          def show do
          Enum.each(unquote(arg), fn x -> IO.inspect x end)
          end
          end
          end

          end

          defmodule Test do
          @data_types [:x, :y, :z]

          require My
          My.go(@data_types)

          def start do
          show()
          end

          end


          In iex:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
          Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

          iex(1)> Test.start()
          :x
          :y
          :z
          :ok

          iex(2)>


          If instead, you try this:



          defmodule My do
          defmacro go(arg) do
          Enum.each(arg, fn x -> IO.inspect x end)
          end

          end

          defmodule Test do
          require My
          @data_types [:x, :y, :z]
          My.go(@data_types)

          def start do
          show()
          end
          end


          then in iex you'll get an error:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

          ** (Protocol.UndefinedError) protocol Enumerable not implemented for {:@, [line: 11], [{:data_types, [line: 11], nil}]}
          (elixir) lib/enum.ex:1: Enumerable.impl_for!/1
          (elixir) lib/enum.ex:141: Enumerable.reduce/3
          (elixir) lib/enum.ex:1919: Enum.each/2
          expanding macro: My.go/1
          my.exs:11: Test (module)


          That error occurs because you are trying to enumerate an ast:



          {:@, [line: 11], [{:data_types, [line: 11], nil}]}


          which is not a list (or any other enumerable--it's obviously a tuple!). Remember that arguments to macros are ast's.



          The reason this works:



            defmacro go(arg) do

          quote do
          def show do
          Enum.each(unquote(arg), fn x -> IO.inspect x end)
          end
          end

          end


          is because quote() creates an ast, and unquote(arg) injects some other ast into the middle of the ast. The macro call My.go(@data_types) executes at compile time, and elixir replaces the macro call with the ast returned by My.go(), which is a function definition named show().






          share|improve this answer


























          • I want to pass module attribute values as an argument to the macro, not access it directly (which I know is easy)

            – Sheharyar
            Jan 2 at 1:52











          • Updated the question title, though I thought the examples in the question already made that pretty clear :)

            – Sheharyar
            Jan 2 at 1:52













          • @Sheharyar, See additional example at the bottom of my answer.

            – 7stud
            Jan 2 at 2:22











          • Your example works because you're defining a method in the same module. It still doesn't solve the problem because quote(do: unquote(@attr)) is still @attr and not it's value. Most library macros generate a new module at compile-time which will not be able to read the value of the module attr passed as arg with this.

            – Sheharyar
            Jan 2 at 3:01
















          0















          But almost always get an error...




          I'm able to access a module attribute inside a macro in this example:



          defmodule My do
          @data_types [:x, :y, :z]

          defmacro go() do
          quote do
          def types() do
          unquote(@data_types)
          end
          end
          end
          end

          defmodule Test do
          require My
          My.go()

          def start do
          types()
          end
          end


          In iex:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

          Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

          iex(1)> Test.start()
          [:x, :y, :z]

          iex(2)>


          Response to comment:



          Here's an example that passes a module attribute as an argument to a macro function call:



          defmodule My do

          defmacro go(arg) do
          quote do
          def show do
          Enum.each(unquote(arg), fn x -> IO.inspect x end)
          end
          end
          end

          end

          defmodule Test do
          @data_types [:x, :y, :z]

          require My
          My.go(@data_types)

          def start do
          show()
          end

          end


          In iex:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
          Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

          iex(1)> Test.start()
          :x
          :y
          :z
          :ok

          iex(2)>


          If instead, you try this:



          defmodule My do
          defmacro go(arg) do
          Enum.each(arg, fn x -> IO.inspect x end)
          end

          end

          defmodule Test do
          require My
          @data_types [:x, :y, :z]
          My.go(@data_types)

          def start do
          show()
          end
          end


          then in iex you'll get an error:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

          ** (Protocol.UndefinedError) protocol Enumerable not implemented for {:@, [line: 11], [{:data_types, [line: 11], nil}]}
          (elixir) lib/enum.ex:1: Enumerable.impl_for!/1
          (elixir) lib/enum.ex:141: Enumerable.reduce/3
          (elixir) lib/enum.ex:1919: Enum.each/2
          expanding macro: My.go/1
          my.exs:11: Test (module)


          That error occurs because you are trying to enumerate an ast:



          {:@, [line: 11], [{:data_types, [line: 11], nil}]}


          which is not a list (or any other enumerable--it's obviously a tuple!). Remember that arguments to macros are ast's.



          The reason this works:



            defmacro go(arg) do

          quote do
          def show do
          Enum.each(unquote(arg), fn x -> IO.inspect x end)
          end
          end

          end


          is because quote() creates an ast, and unquote(arg) injects some other ast into the middle of the ast. The macro call My.go(@data_types) executes at compile time, and elixir replaces the macro call with the ast returned by My.go(), which is a function definition named show().






          share|improve this answer


























          • I want to pass module attribute values as an argument to the macro, not access it directly (which I know is easy)

            – Sheharyar
            Jan 2 at 1:52











          • Updated the question title, though I thought the examples in the question already made that pretty clear :)

            – Sheharyar
            Jan 2 at 1:52













          • @Sheharyar, See additional example at the bottom of my answer.

            – 7stud
            Jan 2 at 2:22











          • Your example works because you're defining a method in the same module. It still doesn't solve the problem because quote(do: unquote(@attr)) is still @attr and not it's value. Most library macros generate a new module at compile-time which will not be able to read the value of the module attr passed as arg with this.

            – Sheharyar
            Jan 2 at 3:01














          0












          0








          0








          But almost always get an error...




          I'm able to access a module attribute inside a macro in this example:



          defmodule My do
          @data_types [:x, :y, :z]

          defmacro go() do
          quote do
          def types() do
          unquote(@data_types)
          end
          end
          end
          end

          defmodule Test do
          require My
          My.go()

          def start do
          types()
          end
          end


          In iex:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

          Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

          iex(1)> Test.start()
          [:x, :y, :z]

          iex(2)>


          Response to comment:



          Here's an example that passes a module attribute as an argument to a macro function call:



          defmodule My do

          defmacro go(arg) do
          quote do
          def show do
          Enum.each(unquote(arg), fn x -> IO.inspect x end)
          end
          end
          end

          end

          defmodule Test do
          @data_types [:x, :y, :z]

          require My
          My.go(@data_types)

          def start do
          show()
          end

          end


          In iex:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
          Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

          iex(1)> Test.start()
          :x
          :y
          :z
          :ok

          iex(2)>


          If instead, you try this:



          defmodule My do
          defmacro go(arg) do
          Enum.each(arg, fn x -> IO.inspect x end)
          end

          end

          defmodule Test do
          require My
          @data_types [:x, :y, :z]
          My.go(@data_types)

          def start do
          show()
          end
          end


          then in iex you'll get an error:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

          ** (Protocol.UndefinedError) protocol Enumerable not implemented for {:@, [line: 11], [{:data_types, [line: 11], nil}]}
          (elixir) lib/enum.ex:1: Enumerable.impl_for!/1
          (elixir) lib/enum.ex:141: Enumerable.reduce/3
          (elixir) lib/enum.ex:1919: Enum.each/2
          expanding macro: My.go/1
          my.exs:11: Test (module)


          That error occurs because you are trying to enumerate an ast:



          {:@, [line: 11], [{:data_types, [line: 11], nil}]}


          which is not a list (or any other enumerable--it's obviously a tuple!). Remember that arguments to macros are ast's.



          The reason this works:



            defmacro go(arg) do

          quote do
          def show do
          Enum.each(unquote(arg), fn x -> IO.inspect x end)
          end
          end

          end


          is because quote() creates an ast, and unquote(arg) injects some other ast into the middle of the ast. The macro call My.go(@data_types) executes at compile time, and elixir replaces the macro call with the ast returned by My.go(), which is a function definition named show().






          share|improve this answer
















          But almost always get an error...




          I'm able to access a module attribute inside a macro in this example:



          defmodule My do
          @data_types [:x, :y, :z]

          defmacro go() do
          quote do
          def types() do
          unquote(@data_types)
          end
          end
          end
          end

          defmodule Test do
          require My
          My.go()

          def start do
          types()
          end
          end


          In iex:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

          Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

          iex(1)> Test.start()
          [:x, :y, :z]

          iex(2)>


          Response to comment:



          Here's an example that passes a module attribute as an argument to a macro function call:



          defmodule My do

          defmacro go(arg) do
          quote do
          def show do
          Enum.each(unquote(arg), fn x -> IO.inspect x end)
          end
          end
          end

          end

          defmodule Test do
          @data_types [:x, :y, :z]

          require My
          My.go(@data_types)

          def start do
          show()
          end

          end


          In iex:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
          Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)

          iex(1)> Test.start()
          :x
          :y
          :z
          :ok

          iex(2)>


          If instead, you try this:



          defmodule My do
          defmacro go(arg) do
          Enum.each(arg, fn x -> IO.inspect x end)
          end

          end

          defmodule Test do
          require My
          @data_types [:x, :y, :z]
          My.go(@data_types)

          def start do
          show()
          end
          end


          then in iex you'll get an error:



          ~/elixir_programs$ iex my.exs
          Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]

          ** (Protocol.UndefinedError) protocol Enumerable not implemented for {:@, [line: 11], [{:data_types, [line: 11], nil}]}
          (elixir) lib/enum.ex:1: Enumerable.impl_for!/1
          (elixir) lib/enum.ex:141: Enumerable.reduce/3
          (elixir) lib/enum.ex:1919: Enum.each/2
          expanding macro: My.go/1
          my.exs:11: Test (module)


          That error occurs because you are trying to enumerate an ast:



          {:@, [line: 11], [{:data_types, [line: 11], nil}]}


          which is not a list (or any other enumerable--it's obviously a tuple!). Remember that arguments to macros are ast's.



          The reason this works:



            defmacro go(arg) do

          quote do
          def show do
          Enum.each(unquote(arg), fn x -> IO.inspect x end)
          end
          end

          end


          is because quote() creates an ast, and unquote(arg) injects some other ast into the middle of the ast. The macro call My.go(@data_types) executes at compile time, and elixir replaces the macro call with the ast returned by My.go(), which is a function definition named show().







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jan 2 at 2:50

























          answered Jan 2 at 1:50









          7stud7stud

          30.3k96587




          30.3k96587













          • I want to pass module attribute values as an argument to the macro, not access it directly (which I know is easy)

            – Sheharyar
            Jan 2 at 1:52











          • Updated the question title, though I thought the examples in the question already made that pretty clear :)

            – Sheharyar
            Jan 2 at 1:52













          • @Sheharyar, See additional example at the bottom of my answer.

            – 7stud
            Jan 2 at 2:22











          • Your example works because you're defining a method in the same module. It still doesn't solve the problem because quote(do: unquote(@attr)) is still @attr and not it's value. Most library macros generate a new module at compile-time which will not be able to read the value of the module attr passed as arg with this.

            – Sheharyar
            Jan 2 at 3:01



















          • I want to pass module attribute values as an argument to the macro, not access it directly (which I know is easy)

            – Sheharyar
            Jan 2 at 1:52











          • Updated the question title, though I thought the examples in the question already made that pretty clear :)

            – Sheharyar
            Jan 2 at 1:52













          • @Sheharyar, See additional example at the bottom of my answer.

            – 7stud
            Jan 2 at 2:22











          • Your example works because you're defining a method in the same module. It still doesn't solve the problem because quote(do: unquote(@attr)) is still @attr and not it's value. Most library macros generate a new module at compile-time which will not be able to read the value of the module attr passed as arg with this.

            – Sheharyar
            Jan 2 at 3:01

















          I want to pass module attribute values as an argument to the macro, not access it directly (which I know is easy)

          – Sheharyar
          Jan 2 at 1:52





          I want to pass module attribute values as an argument to the macro, not access it directly (which I know is easy)

          – Sheharyar
          Jan 2 at 1:52













          Updated the question title, though I thought the examples in the question already made that pretty clear :)

          – Sheharyar
          Jan 2 at 1:52







          Updated the question title, though I thought the examples in the question already made that pretty clear :)

          – Sheharyar
          Jan 2 at 1:52















          @Sheharyar, See additional example at the bottom of my answer.

          – 7stud
          Jan 2 at 2:22





          @Sheharyar, See additional example at the bottom of my answer.

          – 7stud
          Jan 2 at 2:22













          Your example works because you're defining a method in the same module. It still doesn't solve the problem because quote(do: unquote(@attr)) is still @attr and not it's value. Most library macros generate a new module at compile-time which will not be able to read the value of the module attr passed as arg with this.

          – Sheharyar
          Jan 2 at 3:01





          Your example works because you're defining a method in the same module. It still doesn't solve the problem because quote(do: unquote(@attr)) is still @attr and not it's value. Most library macros generate a new module at compile-time which will not be able to read the value of the module attr passed as arg with this.

          – Sheharyar
          Jan 2 at 3:01


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Stack Overflow!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54000082%2fpass-module-attribute-as-an-argument-to-macros%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Can a sorcerer learn a 5th-level spell early by creating spell slots using the Font of Magic feature?

          Does disintegrating a polymorphed enemy still kill it after the 2018 errata?

          A Topological Invariant for $pi_3(U(n))$