Incomprehensible compilation error: undefined function

Hi guys,

I am trying to use an Queue with Agents in the next program:

defmodule TestMyQueue do

  require EQueue

  @times 1..100
    
  collection =  EQueue.new()

  {:ok, queue} = Agent.start_link fn -> collection end
    
  Enum.each(@times, fn item ->
    case rem(item, 5) == 0 do
      true  -> pop_agent(queue)
      false -> Agent.update(queue, &push(&1, item))
    end
  end)
    
  Agent.stop(queue)
 
  defp pop([]), do: {:empty, []}
  defp pop([h|t] = from) when is_list(from), do: {:value, h, t} 
  defp pop(queue = %EQueue{}), do: EQueue.pop(queue)

  defp pop_agent(agent), do: Agent.get_and_update(agent, fn state ->
    case pop(state) do
      {:value, item, queue} -> {item, queue}
      {:empty, queue} -> {:empty, queue}
    end
  end)

  defp push(collection, item) when is_list(collection), do: collection ++ [item]
  defp push(collection = %EQueue{}, item), do: EQueue.push(collection, item)  
  
end

and I get the next compilation error: (CompileError) lib/tst.ex:29: undefined function pop_agent/1

Can anyone explain what I am doing wrong?

Thanks in advance,

Thiel Chang

Maybe it’s because you call pop_agent before you define it? Compilation happens from top to bottom, I think.

Could you explain what you are trying to achieve with

  require EQueue

  @times 1..100
    
  collection =  EQueue.new()

  {:ok, queue} = Agent.start_link fn -> collection end
    
  Enum.each(@times, fn item ->
    case rem(item, 5) == 0 do
      true  -> pop_agent(queue)
      false -> Agent.update(queue, &push(&1, item))
    end
  end)
    
  Agent.stop(queue)

I don’t see it produce any side effects on the module.

I’m going to answer this from a purely technical perspective.

Lines 7 - 18 are run at compile-time. pop_agent/1 won’t exist at the point it’s invoked.

So @idi527 is correct there.

You probably want to put lines 7 - 18 in a function?

1 Like

Hi guys,

You were all right. I redefined the External and Internal APIś and now it works!

defmodule EQueTest do
  use ExUnit.Case

  require EQueue
  
  @name __MODULE__
  
  # External API's
  
  def start_link(queue) do
    Agent.start_link(fn -> queue end)
  end
  
  def start_link(queue, @name) do
    Agent.start_link(fn -> queue end, name: @name)
  end
  
  def push_item(queue,item) do
     Agent.update(queue, &push(&1, item))
  end
  
  def pop_item(queue), do: Agent.get_and_update(queue, fn state ->
    case pop(state) do
      {:value, item, queue} -> {item, queue}
      {:empty, queue} -> {:empty, queue}
    end
  end)

  # Internal API's
  
  defp push(collection, item) when is_list(collection), do: collection ++ [item]
  defp push(collection = %EQueue{}, item), do: EQueue.push(collection, item)
  
  defp pop([]), do: {:empty, []}
  defp pop([h|t] = from) when is_list(from), do: {:value, h, t}
  defp pop(queue = %EQueue{}), do: EQueue.pop(queue)

  test "Test EQueue's push and pop" do
 
  {_result, queue} = start_link(EQueue.new())
 
  push_item(queue, 1)
 
  push_item(queue, 2)
 
  assert pop_item(queue) == 1
 
  end
 
end

Many thanks,

Thiel Chang

1 Like