0
votes

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

2 Answers

3
votes

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.

0
votes

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