Pass Module attribute as an argument to Macros
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
add a comment |
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
add a comment |
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
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
elixir
edited Jan 2 at 3:02
Sheharyar
asked Jan 2 at 0:42
SheharyarSheharyar
46k12110164
46k12110164
add a comment |
add a comment |
2 Answers
2
active
oldest
votes
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.
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 withoutMacro.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 doesbad/1
provide that is not provided byIO.inspect(param, label: "👍 Passed")
insidegood/1
?
– 7stud
Jan 4 at 1:31
|
show 1 more comment
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()
.
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 becausequote(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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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.
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 withoutMacro.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 doesbad/1
provide that is not provided byIO.inspect(param, label: "👍 Passed")
insidegood/1
?
– 7stud
Jan 4 at 1:31
|
show 1 more comment
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.
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 withoutMacro.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 doesbad/1
provide that is not provided byIO.inspect(param, label: "👍 Passed")
insidegood/1
?
– 7stud
Jan 4 at 1:31
|
show 1 more comment
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.
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.
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 withoutMacro.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 doesbad/1
provide that is not provided byIO.inspect(param, label: "👍 Passed")
insidegood/1
?
– 7stud
Jan 4 at 1:31
|
show 1 more comment
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 withoutMacro.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 doesbad/1
provide that is not provided byIO.inspect(param, label: "👍 Passed")
insidegood/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
|
show 1 more comment
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()
.
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 becausequote(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
add a comment |
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()
.
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 becausequote(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
add a comment |
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()
.
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()
.
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 becausequote(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
add a comment |
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 becausequote(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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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