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

I would like to know what I can do to get rid of the “incompatible types” warnings that are cluttering my phoenix server logs - I would willingly accept “operator error” as the cause if I could learn how to prevent the warnings. This is essentially the same as Destructuring a 2-tuple throws linter warning but still works. Incompatible types: tuple() !~ {var1, var2} - I am also using vscode and ElixirLS - in that post, one comment said that the problem would be fixed elixir 1.13 - I’m using 1.15. The phoenix app fine despite the warnings in the logs

Any help/direction is appreciated.

++++++++++++++++++++++++++
warning: incompatible types:

tuple() !~ {var1, var2, var3}

in expression:

# lib/dancePHX/dance_parser.ex:280
{ol, bts, d_bts} = acc

where “acc” was given the type tuple() in:

# lib/dancePHX/dance_parser.ex:278
elem(acc, 1)

where “acc” was given the type {var1, var2, var3} in:

# lib/dancePHX/dance_parser.ex:280
{ol, bts, d_bts} = acc

++++++++++++++++++++++++++
the code that’s generating this warning:

def _gen_phrases(el, acc) when elem(acc, 1) < 16 do <<<— line 278
[fig, b, _c] = el
{ol, bts, d_bts} = acc

...

end

There are also _gen_phrases(el, acc) functions using elem(acc, 1) == 16 and elem(acc, 1) > 16 which generate the same warning.

I’m using phoenix 1.6.11 and elixir 1.15.6.

++++++++++++++++++++++++++
$ elixir -v
Erlang/OTP 26 [erts-14.1] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] [jit:ns]
Elixir 1.15.6 (compiled with Erlang/OTP 26)

Well, let me just say that I had no clue that function names could be prefixed with underscores.

2 Likes

I assume it’s warning that the basic tuple() type denotes tuples of any size and you’re matching it to a 3-element tuple, where it could fail.

A tuple in a pattern will match only tuples of the same size
Patterns and Guards — Elixir v1.15.6

You could explicitly match the correct shape in the function definitions:

def _gen_phrases(el, {ol, bts, d_bts} = acc) when bts == 16
# or alternatively 
# def _gen_phrases(el, {ol, bts = 16, d_bts} = acc)

def _gen_phrases(el, {ol, bts, d_bts} = acc) when bts < 16

def _gen_phrases(el, {ol, bts, d_bts} = acc) when bts > 16
# or leave out the guard as the other 2 clauses match everything <=16
1 Like

Yep, they’re conventionally considered to be “hidden” (similar to double-underscore-prefixed struct fields) and will never be implicitly imported, auto-displayed in exdoc, or listed in iex helpers.

1 Like

It was under our noses all along…

Function names may also start with an underscore. Such functions are never imported by default…
Naming conventions — Elixir v1.16.3

It’s one of those things that we kinda learn about by practice and intuition, like every module’s __info__/1 or the venerable __MODULE__/0 and __ENV__/0, that we could actually apply to our own code but rarely do.

1 Like

Problem solved. Thank you 03juan for pointing me to the tuple size. I added " and tuple_size == 3" to the when clause and the type warnings are gone.

Even more to the point __using__/1, __before_compile__/1, __after_compile__/2, etc!

EDIT: wait, no not more to the point! TIL __MODULE__/0 is a function :sweat_smile:

EDIT 2: wait… is it? I’m just going to shut up now, lol.

1 Like

Yeah I’ve seen it many times, it just never occurred to me to use them. Not even once. :face_with_raised_eyebrow:

Well, a Kernel.SpecialForms macro, so… kinda? :man_shrugging: