Elixir v1.19 then compile warning

Following this and I’m wondering the deets about it. :nerd_face:

I made a simple recreation:

  def hello(map) do
    map
    |> Map.to_list()
    |> Enum.reduce_while(%{}, fn {key, value}, acc ->
      if value == 2 do
        {:halt, :error}
      else
        {:cont, Map.put(acc, key, value)}
      end
    end)
    |> then(fn
      %{a: a} = data when a == 3 ->
        %{data | b: a}

      data ->
        data
    end)
    |> case do
      :error -> "It's error"
      data -> "It's okay #{data.a}"
    end
  end

This produces the warning:

    warning: the following clause will never match:

        :error

    because it attempts to match on the result of:

        (fn
           %{a: a} = data when a == 3 -> %{data | b: a}
           data -> data
         end).(
          Enum.reduce_while(Map.to_list(map), %{}, fn {key, value}, acc ->
            if value == 2 do
              {:halt, :error}
            else
              {:cont, Map.put(acc, key, value)}
            end
          end)
        )

    which has type:

        dynamic(%{..., a: term(), b: term()})

    typing violation found at:
    │
 33 │       :error -> "It's error"
    │       ~~~~~~~~~~~~~~~~~~~~~~
    │
    └─ lib/then_case.ex:33: ThenCase.hello/1

It’s like putting the first clause in then makes the compiler think the data variable is always dynamic(%{..., a: term(), b: term()}).

Removing that clause or moving it inside case (inside then or removing then and just case) resolves the warning.

It seems the compiler does not like cases made with then. :smiley:
I’m just curious, is this the expected behavior and what’s the reason behind it? :bug:

2 Likes

That’s a bug in the type system, I will take it from here. Thank you!

7 Likes