joshua.aug
"The pattern can never match the type." error from dialyzer
Im getting this error from dialyzer “The pattern can never match the type.”
Here’s some sample code
@spec check_rating_to_play(any(), any()) :: :ok | {:error, String.t()}
def check_rating_to_play(user_id, consul_state) do
@spec check_rank_to_play(any(), any()) :: :ok | {:error, String.t()}
def check_rank_to_play(user, consul_state) do
This function is called like this
rating_check_result = LobbyRestrictions.check_rating_to_play(userid, state)
rank_check_result = LobbyRestrictions.check_rank_to_play(user, state)
cond do
rating_check_result != :ok ->
# Send message
{_, msg} = rating_check_result
CacheUser.send_direct_message(get_coordinator_userid(), userid, msg)
false
rank_check_result != :ok ->
# Send message
{_, msg} = rank_check_result
CacheUser.send_direct_message(get_coordinator_userid(), userid, msg)
false
Is dialyzer saying that the first condition is always true? I’m confused.
Marked As Solved
al2o3cr
Dialyzer has inferred that check_rating_to_play and check_rank_to_play can ONLY return :ok, so it deduces that those branches of the cond are dead.
The issue is sneaky, but ultimately comes from get_failed_rating_check_text and get_failed_rank_check_text returning the wrong type.
A walkthrough of what’s happening. Start in this code, from check_rating_to_play:
cond do
state.minimum_rating_to_play != nil and player_rating < state.minimum_rating_to_play ->
msg = get_failed_rating_check_text(player_rating, state, rating_type)
{:error, msg}
# maximum clause omitted
true ->
# All good
:ok
end
What’s the type of msg? It’s the return type of get_failed_rating_check_text.
What’s that type? It’s [String.t()]!
def get_failed_rating_check_text(player_rating, consul_state, rating_type) do
bounds = get_rating_bounds_for_title(consul_state)
player_rating_text = player_rating |> Decimal.from_float() |> Decimal.round(2)
[
@splitter,
"You don't meet the rating requirements for this lobby (#{bounds}). Your #{rating_type} match rating is #{player_rating_text}. Learn more about rating here:",
"https://www.beyondallreason.info/guide/rating-and-lobby-balance#openskill"
]
end
Dialyzer sees this and infers that the minimum_rating_to_play branch of the cond would make the function return {:error, [String.t()]}.
The function is not specced to return that type, so Dialyzer infers that that branch does not execute, so check_rating_to_play “always” returns :ok.
When I run Dialyzer locally, it also complains about get_coordinator_userid not being called - further evidence it has deduced that those branches are dead.
Replacing the lists in get_failed_rating_check_text and get_failed_rank_check_text with a literal string makes all three messages go away, verifying the diagnosis.
Possible fixes:
-
join the strings together in
get_failed_rating_check_textetc -
widen the type of
check_rating_to_playetc to:ok, {:error, iodata()}
Also Liked
LostKobrakai
To dialyzer doesn‘t matter what it can theoretically return. The only thing that matters is what dialyzer inferred for it to return. And if inference says it only ever returns ˋ:okˋ then the error message makes sense. Now the question becomes why dialyzer would infer that ˋ:okˋ is the only possible return value for those functions.
kokolegorille
It looks the state is never nil, so :ok can never be reached…
cond do
state.minimum_rating_to_play != nil and player_rating < state.minimum_rating_to_play ->
msg = get_failed_rating_check_text(player_rating, state, rating_type)
{:error, msg}
state.maximum_rating_to_play != nil and player_rating > state.maximum_rating_to_play ->
msg = get_failed_rating_check_text(player_rating, state, rating_type)
{:error, msg}
true ->
# All good
:ok
In this file…
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








