#[Dialyzer error] improper_list_constr

13 messages · Page 1 of 1 (latest)

supple ridge
#

Hi, I've been working with an Elixir project for a couple of months now. And decided to clean up the house by using Dialyzer (Dialyxir).
By doing so I found this error which I'm failing to understand:

improper_list_constr
List construction (cons) will produce an improper list, because its second argument is map().

The code in cuestion is this:

    Enum.reduce_while(tasks, [], fn %{type: type} = task, changes_so_far ->
      execute_task(task, context, changes_so_far)
      |> case do
        {:ok, intent} ->
          {:cont, [%{type: type, intent: intent} | changes_so_far]} # This line has the error

        {:error, _} ->
          {:halt, {:error, task}}
      end
    end)

The code runs. And the error dissapears by changing {:cont, [%{type: type, intent: intent} | []]} , so it indeed thinks that changes_do_far is a map(), which shouldn't be the case as the initial value it's []. Has anybody encountered a similar situation? Any help would be greatly appreciated! 😃

bitter mountain
supple ridge
#

No, it's just the accumulator of the reducer

bitter mountain
bitter mountain
supple ridge
#

It's the equivalent to the acc in this sample code

Enum.reduce_while(1..100, 0, fn x, acc ->
     if x < 5, do: {:cont, acc + x}, else: {:halt, acc}
end)
bitter mountain
#

what's the typespec/definition for execute_task

#

I know that it shouldn't have anything to do with that function, the code you have, at face value, doesn't look like it should trigger that warning, so I suspect dialyzer thinks changes_so_far is a map by inferring it from execute_task

#

it's just a hunch, but maybe there's something off there

supple ridge
#

You got it right, it has something to do with it. I replaced the call in this way, and the error is gone:

    Enum.reduce_while(tasks, [], fn %{type: type} = task, changes_so_far ->
      foo()
      |> case do
        {:ok, intent} ->
          {:cont, [%{type: type, intent: intent} | changes_so_far]} # This line has the error

        {:error, _} ->
          {:halt, {:error, task}}
      end
    end)

  defp foo() do
    random = :rand.uniform(2)
    cond do
      rem(random, 2) == 0 ->
        {:ok, :intent}
      true ->
        {:error, :reason}
    end
  end
#

I'll look into it, but still the error doesn't seem to make sense to me

#

Very interesting. So the issue was that execute_task was mistakenly typespec-ed, like this: @spec execute_task(Task.t(), Context.t(), map()) :: {:ok, Intent.t()} | {:error, term()} .
I'm assuming that Dialyzer saw that and thought that changes_so_far was a map instead of a list. It seems counterintuitive, as I would expect the definition of a literal list as it's initial value to take precedence on the inference process, and mark the execute_task spec as the error instead. 🤔

#

Thank you so much! I'll take this into account while looking into the other errors 🤝