Module could not be found

Hey there,

I am currently doing this years Advent of Code and wanted to do it using Elixir, to learn a little bit about functional programming. I set up my local Elixir project the following way:

[xxx@desktop-arch advent_of_code_2023]$ ls
_build  data  deps  lib  mix.exs  mix.lock  README.md  test

The lib directory contains files for each day, the input data for each day is in the data directory. In the test directory, I have a advent_of_code2023.exs file that tests the results of all days:

defmodule AdventOfCode2023Test do
  use ExUnit.Case
  doctest AdventOfCode2023

  @moduletag timeout: :infinity

  test "day_1" do
    assert AdventOfCode2023.Day1.task1() == 57346
    assert AdventOfCode2023.Day1.task2() == 57345
  end
#...
end

Until day 8, that worked flawlessly (I know I am a little bit behind :D). I could simply test everything using mix test. However, in day 8 I want to use an external library for the lcm function, to see how that works in Elixir. I therefore did the following in the mix.exs:

  defp deps do
    [
      {:math, "~> 0.7.0"}
    ]
  end

Now in my day8.ex I did the following:

defmodule AdventOfCode2023.Day8 do
  import Math

  def task2 do
    [instructions, map, all_ending_a] = load_instruction_and_map()

    periods_for_reaching_xxZ =
      Enum.map(all_ending_a, &get_period_for_being_in_xxZ({instructions, 0, 0, 0}, {map, &1}))

    Enum.reduce(
      Enum.slice(periods_for_reaching_xxZ, 1, Enum.count(periods_for_reaching_xxZ)),
      Enum.at(periods_for_reaching_xxZ, 0),
      fn current_period, lcm -> Math.lcm(current_period, lcm) end
    )
  end
#...
end

Now my problem: mix test errors out because it can not find the Math module! The weird thing is, it works once after rebuilding, but the second mix test call fails:

[xxx@desktop-arch advent_of_code_2023]$ ls
_build  data  deps  lib  mix.exs  mix.lock  README.md  test
[xxx@desktop-arch advent_of_code_2023]$ rm -rf _build/
[xxx@desktop-arch advent_of_code_2023]$ rm -rf deps/
[xxx@desktop-arch advent_of_code_2023]$ mix deps.get
Resolving Hex dependencies...
Resolution completed in 0.005s
Unchanged:
  math 0.7.0
* Getting math (Hex package)
[xxx@desktop-arch advent_of_code_2023]$ mix compile
==> math
Compiling 2 files (.ex)
Generated math app
==> advent_of_code_2023
Compiling 9 files (.ex)
warning: unused import Math
  lib/day8.ex:2

Generated advent_of_code_2023 app
[xxx@desktop-arch advent_of_code_2023]$ mix test
==> math
Compiling 2 files (.ex)
Generated math app
==> advent_of_code_2023
Compiling 9 files (.ex)
warning: unused import Math
  lib/day8.ex:2

Generated advent_of_code_2023 app
........
Finished in 0.1 seconds (0.00s async, 0.1s sync)
8 tests, 0 failures

Randomized with seed 764254
[xxx@desktop-arch advent_of_code_2023]$ mix test
warning: unused import Math
  lib/day8.ex:2

......

  1) test day_8 (AdventOfCode2023Test)
     test/advent_of_code2023_test.exs:42
     ** (UndefinedFunctionError) function Math.lcm/2 is undefined (module Math is not available)
     code: assert AdventOfCode2023.Day8.task2() == 20220305520997
     stacktrace:
       (math 0.7.0) Math.lcm(18827, 17141)
       (elixir 1.15.7) lib/enum.ex:2510: Enum."-reduce/3-lists^foldl/2-0-"/3
       test/advent_of_code2023_test.exs:44: (test)

.
Finished in 0.1 seconds (0.00s async, 0.1s sync)
8 tests, 1 failure

Randomized with seed 380277

So my question is now whether I am doing something wrong here, but if thats the case why is it working once? Except this thread (Load module during Test) I did not found anyone with module problems only when testing. I tried the elixirc_paths approach in the thread but it did not help, probably because I am not really knowing what I am doing.

I would really appreciate any help with this, this is driving my slowly insane now :smiley: At least it works once after rebuild, so I could still claim the golden stars for day 8.

Best regards,
Christoph

1 Like

It would make the debugging a lot easier, if you could provide us link to the git repository.

You are probably right… I made it public here: GitHub - Chr1s603/advent_of_code_2023: Advent of Code 2023

Just don’t judge the code too much, as I said I am pretty new to functional programming!

1 Like

OK, so the problem was in mix.exs on line 18, where there should be extra_applications: [:logger] instead of applications: [:logger].

It looks like you experimented there a bit and left it in incorrect state. :slight_smile:

Anyway, this is something I am a bit confused with too. See here for more info.

Some tips regarding the coding style that you may find useful:

  • do not use capital letters in the variable names
  • you can use Enum.slice(periods_for_reaching_xxZ, 1, -1) instead of Enum.slice(periods_for_reaching_xxZ, 1, Enum.count(periods_for_reaching_xxZ)) or even use Enum.drop(periods_for_reaching_xxZ, 1)
  • you can use Enum.filter(fn [pos, _] -> String.ends_with?(pos, "A") end) instead of Enum.reject(fn [pos, _] -> not String.ends_with?(pos, "A") end) I feel like there’s a double negation in the first variant
  • you can write
if reached_cnt >= 2 do
  idx - last_idx_when_reaching_pos_with_z
else
  get_period_for_being_in_xxZ(
    {instructions, idx + 1, idx, reached_cnt},
    {map, next_pos}
  )
end

instead of

cond do
  reached_cnt >= 2 ->
    idx - last_idx_when_reaching_pos_with_z

  true ->
    get_period_for_being_in_xxZ(
      {instructions, idx + 1, idx, reached_cnt},
      {map, next_pos}
    )
end
  • you can write
Map.new(map_str, fn [pos, [l, r]] ->
  {String.to_atom(pos), [String.to_atom(l), String.to_atom(r)]}
end)

instead of

map_str
|> Enum.map(fn [pos, [l, r]] ->
  [String.to_atom(pos), [String.to_atom(l), String.to_atom(r)]]
end)
|> Map.new(fn [pos, lr] -> {pos, lr} end)
1 Like

Thank you very much, that fixed it! Interesting that this line resulted in this… weird behaviour :smiley:

Also thanks for the free coding tips, I integrated them all and now it looks a lot cleaner!

1 Like