defmodule MyModule do
require Logger
def testing do
sport_color = "pink"
case Ultraviolet.new(sport_color) do
{:error, m} -> Logger.error("should_not_happen", reason: m)
{:ok, color} -> color
end
end
end
error :
lib/my_module.ex:9:pattern_match
The pattern can never match the type.
Pattern:
{:ok, _color}
Type:
{:error, _}
________________________________________________________________________________
done (warnings were emitted)
Halting VM with exit status 2
Here’s a repository where I can reproduce the issue:
The warning doesn’t really make much sense to me, as I’ve used Color.new() in a variety of tests without encountering error cases, so I don’t know why dialyzer would see no path forward but the error case for a case I’ve definitely tested. Regardless, I’ve been working at this for a few hours now and I can’t seem to resolve this issue. Here’s what I’ve tried so far:
fixing all the dialyzer warnings / errors in Ultraviolet. While the published version still has warnings, the main branch does not. I’ve edited mix.exs on the example repository to point to my local copy with all the warnings resolved.
expanded the metaprogramming construct I use to generate function signatures for the W3CX11 named colors.
overriding the function to force a return type of {:ok, %Color{}} in an attempt to get something working.
None of these seem to work. Does anyone have any suggestions for how I might solve this problem? Thanks!
Dialyzer doesn‘t care for what happens when the code actually runs. It cannot run it. It just cares for inferred types and how manual specs improve them.
You‘d want to figure out where dialyzer goes of the rails. One of its problems is that it will report the violation at a higher place in a callstack than where the source of the problem is. E.g. some of the helper functions to construct a color might have type inferred or speced wrongly.
This will likely not help you at all but here’s what I have on a freshly cloned project (Elixir 1.18.1-OTP-27, Erlang 27.2):
❯ mix do deps.get, compile
Resolving Hex dependencies...
Resolution completed in 0.009s
Unchanged:
decimal 2.3.0
dialyxir 1.4.5
erlex 0.2.7
ultraviolet 0.1.0
* Getting ultraviolet (Hex package)
* Getting dialyxir (Hex package)
* Getting erlex (Hex package)
* Getting decimal (Hex package)
==> decimal
Compiling 4 files (.ex)
Generated decimal app
==> ultraviolet
Compiling 14 files (.ex)
warning: the following clause will never match:
{:ok, colors}
because it attempts to match on the result of:
Ultraviolet.ColorBrewer.colors(palette, count)
which has type:
dynamic({:error, :not_found})
typing violation found at:
│
743 │ {:ok, colors} -> Scale.new(colors, options)
│ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
│
└─ lib/ultraviolet.ex:743: Ultraviolet.scale/2
Generated ultraviolet app
==> erlex
Compiling 1 file (.xrl)
Compiling 1 file (.yrl)
src/erlex_parser.yrl: Warning: conflicts: 27 shift/reduce, 0 reduce/reduce
Compiling 2 files (.erl)
Compiling 1 file (.ex)
Generated erlex app
==> dialyxir
Compiling 67 files (.ex)
Generated dialyxir app
==> ultraviolet_dialyzer
Compiling 2 files (.ex)
Generated ultraviolet_dialyzer app
Note that this might be Elixir’s new type checker being overzealous; 1.18 is the first release where the new type checker is being used in production. If that’s the case then it might be worth escalating to the core Elixir team itself.
I think that particular error is due to the presence of a check for the optional Jason dependency to generate the proper function signatures at compile time (See lib/ultraviolet/color_brewer.ex). Future versions of the library will require 1.18 and use the built-in JSON parser introduced in that release, so this warning should go away.
Okay, this took an embarrassingly long time to figure out, but since I was debugging the issue with the local version of my library, dialyzer did not even check for code changes to update the PLT (see this dialixir issue)! You need to use the --force-check argument in this case, i.e. mix dialyzer --force-check That should update the PLT accordingly. (TIL)