0

I'm going to preface this by saying I am a total newbie in the Elixir world (and functional programming in general). I am currently learning about dictionary types, and came across structs. I defined this one, as per my book's instruction:

defmodule Subscriber do
  defstruct name: "", paid: false, over_18: true
end

After that, the book works in iex for the rest of the chapter, but I wanted mine in a file. I tried to create an instance of this struct (with all values set to default) like so:

sub = %Subscriber{}

This game me a CompileError:

CompileError: cannot access struct Subscriber, the struct was not yet defined or the struct is being accessed in the same context that defines it

It works fine in iex, but like I said I would rather have it in a file. Any chance someone could explain what I'm doing wrong here?

2 Answers 2

5

someone could explain what I'm doing wrong here?

I believe, iex has already explained what’s wrong, I wouldn’t hesitate to repeat: “the struct is being accessed in the same context that defines it”.

it works fine in iex

Yes, because iex is a REPL and it compiles each statement as it’s complete.

Elixir is a compiled language. The compilation unit under normal circumstances is a file. In REPL it’s a single complete statement. Unless the code is compiled, one cannot access it directly (but the deferred calls are still available.)

This would work:

defmodule A, do: defstruct foo: :bar
defmodule B, do: def b, do: IO.inspect %A{}
B.b

Also this would work:

defmodule A, do: defstruct foo: :bar
IO.inspect struct(A)

But the explicit call to %A requires the compiler to know how to treat the following AST (see line 2):

quote do: %A{}                      
#⇒ {:%, [], 
#    [{:__aliases__, [alias: false], [:A]},
#    {:%{}, [], []}]}

While A is just an atom (yes, it’s a plain atom,)

is_atom(A)
#⇒ true

it might be easily injected to any AST and compiled successfully. The second line of the AST above must be expanded and until the struct definition is available to the compiler, it cannot be.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks! Just for a little clarifaction: could you explain what that struct/1 function is actually doing? I couldn't find documentation for it online.
Kernel.struct/2, as it might be easily seen in it’s source code, basically creates a map and updates it with the __struct__ field, no magick.
Thanks! I appreciate the help
0

What I learned about [your specific compile error] of not being able to use a struct in the same file as it is defined, is that typically means you're accessing or using the struct from the outer file's lexical scope.

Simply create your struct instance in another module's function and not in the outer file scope ('context') and then call that function to have it run.

This is my simple example using a struct named Product, in this single file:

defmodule Product do                     
    defstruct [:name, :sku, :upc ]
end

defmodule Main do
    def hello do
        hh = %Product{name: 'dog'}
        IO.puts "Hello, #{hh.name}"
    end
end

Main.hello

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.