Destructuring a 2-tuple throws linter warning but still works. Incompatible types: tuple() !~ {var1, var2}

I am trying to destructure a tuple in a case statement and I am getting a warning that confuses me. It passes my test case, so it works, but the linter is giving me a warning and I’m not sure how to handle it. Am I doing a bad practice? I have a simplified scenario that demonstrates the warning below.

def test(x) do
  case x do
    tuple when is_tuple(tuple) ->
      {v1, v2} = tuple
      IO.puts(v1)
      IO.puts(v2)
  end
end

The warning is

incompatible types:
    tuple() !~ {var1, var2}
in expression:
    # [file path]
    {v1, v2} = x
where "x" was given the type tuple() in:
    # [file path]
    is_tuple(x)
where "x" was given the type {var1, var2} in:
    # [file path]
    {v1, v2} = x
1 Like

I don’t know the source of the warning, but it would be more idiomatic to remember that case clauses are match expressions so you can destructure more simply:

def test(x) do
  case x do
    {v1, v2} ->
      IO.puts(v1)
      IO.puts(v2)
  end
end
4 Likes

What is this linter you are using?
(Whatever it is, it just states, that not all tuples are 2-tuples.)

1 Like

And if the only input you ever expect in your function is an arity-2 tuple, then you can also pattern-match in the function clause itself:

def test({v1, v2}) do
  IO.puts(v1)
  IO.puts(v2)
end
1 Like

Interestingly, the linter shows the warning even with a guard clause of ... when is_tuple(x) and tuple_size(x) == 2.

The linter is elixir-ls via the vscode wrapper for it. The method-level/case-level destructing does not produce the linter warning, which is why I marked the problem as solved (thank you all for your feedback :D). But, it still does seem a curious warning.

Correct. This will be fixed in v1.13.

6 Likes