Question

When quoted using quote do: records aren't converted to tuples containing the record fields:

 iex(1)> quote do: is_bitstring("blah")
 {:is_bitstring, [context: Elixir, import: Kernel], ["blah"]}
 iex(2)> quote do: Computer.new("Test")
 {{:., [], [{:__aliases__, [alias: false], [:Computer]}, :new]}, [], [[name: "Test"]]}
 iex(3)> quote do: Computer.new("Test")
 {{:., [], [{:__aliases__, [alias: false], [:Computer]}, :new]}, [], [[name: "Test"]]}
 iex(4)> c = Computer.new("Test")
 Computer[name: "Test", type: nil, processor: nil, hard_drives: []]
 iex(5)> c
 Computer[name: "Test", type: nil, processor: nil, hard_drives: []]
 iex(6)> quote do: c
 {:c, [], Elixir}

Also, when I try doing this in my code:

defmacro computer([do: code]) do
  # macro login here
  # build computer record based on macro logic
  computer = Computer.new(params)
  quote do: unquote computer
end

I get an error:

** (CompileError) elixir/test/lib/computer_dsl_test.exs: tuples in quoted expressions must have 2 or 3 items, invalid quoted expression: Computer[name: "", type: nil, processor: nil, hard_drives: []]

I thought that records were just tuples with wrappers functions of some sort. The Elixir Getting Started guide states "A record is simply a tuple where the first element is the record module name." Is there something I am missing? Is there a function I can call on a record to get the tuple representation? I am aware of the raw: true option but I am not sure how to use that on an existing record.

Any insights?

Was it helpful?

Solution

Records are tuples. The output you see on the console is just formatted for easier inspection. You can check that records are tuples if you inspect them with raw: true:

iex(1)> defrecord X, a: 1, b: 2

iex(2)> x = X.new
X[a: 1, b: 2]   # This is formatted output. x is really a tuple

iex(3)> IO.inspect x, raw: true
{X, 1, 2}

As can be seen, a record instance is really a tuple. You can also pattern match on it (although I don't recommend this):

iex(4)> {a, b, c} = x

iex(8)> a
X

iex(9)> b
1

iex(10)> c
2

The quote you are mentioning serves completely different purpose. It turns an Elixir expression into AST representation that can be injected into the rest of the AST, most often from the macro. Quote is relevant only in compile time, and as such, it can't even know what is in your variable. So when you say:

quote do: Computer.new("Test")

The result you get is AST representation of the call of the Computer.new function. But the function is not called at this point.

OTHER TIPS

Just reading the error message and the elixir "getting stated" on macro definition it appears that the result of a quote has the form:

In general, each node (tuple) above follows the following format:

{ tuple | atom, list, list | atom }

The first element of the tuple is an atom or another tuple in the same representation;

The second element of the tuple is an list of metadata, it may hold information like the node line number;

The third element of the tuple is either a list of arguments for the function call or an atom. When an atom, 

it means the tuple represents a variable.

Besides the node defined above, there are also five Elixir literals that when quoted return themselves (and not a tuple). They are:

:sum #=> Atoms

1.0 #=> Numbers

[1,2] #=> Lists

"binaries" #=> Strings

{key, value} #=> Tuples with two elements

My guess is that the unquote is the reverse function of quote, and so it expects as argument one of the above forms. This is not the case for the computer record.

I think the unquote is not necessary there (although I didn't try to understand the intent of your code...) and that

defmacro computer([do: code]) do %% why do you need this argument?
    quote do: Computer.new
end

should be ok.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top