Search code examples
elixirelixir-iex

iex, how to reload structs effectively?


I found a minimal example that shows one of the problem that I encounter while learning Elixir.

I write a script:

defmodule P do
  defstruct a: 2
  def geta(%P{a: a}), do: a
end

Use iex "filename" to start an iex session check that it works with P.geta(%P{a: 42})

Then I change the file to

defmodule P do
  defstruct a: 2, b: 4
  def getb(%P{a: a, b: b}), do: b
end

And when I launch iex, it fails:

== Compilation error on file p.ex ==
** (CompileError) p.ex:3: unknown key :b for struct P
    (elixir) src/elixir_map.erl:185: :elixir_map."-assert_struct_keys/5-lc$^0/1-0-"/5
    (elixir) src/elixir_map.erl:62: :elixir_map.translate_struct/4
    (stdlib) lists.erl:1353: :lists.mapfoldl/3

** (exit) shutdown: 1
    (elixir) lib/kernel/parallel_compiler.ex:202: Kernel.ParallelCompiler.handle_failure/5
    (elixir) lib/kernel/parallel_compiler.ex:185: Kernel.ParallelCompiler.wait_for_messages/8
    (elixir) lib/kernel/parallel_compiler.ex:55: Kernel.ParallelCompiler.spawn_compilers/3
       (iex) lib/iex/helpers.ex:168: IEx.Helpers.c/2

I can work around it by removing the content of the file except the definition, launching iex, pasting back the content of the file and relaunching iex. It looks to me as if the old struct is somehow cached by iex.

Two questions:

  • Is this a bug or a design decision?
  • Is there a more clever way to work around it?

Solution

  • The main issue is you have functions on your struct module. That will cause problems like your seeing. You need to separate your struct and functions. You can nest the struct module inside your module if you want to.

    defmodule P do
      defmodule Data do
         defstruct a: 2, b: 4
      end
    
      def geta(%Data{a: a}), do: a
      def getb(%Data{b: b}), do: b
    end
    

    in iex:

    iex> P.geta(%P.Data{a: 10})
    10
    iex> P.getb(%P.Data{a: 3, b: 24})
    24