-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
with
expression support and clause reachability
#85
Comments
Hmm, it seems we're not handling the code generated for the |
Removing the spec or replacing it with |
Ok, here we have some findings: with _value <- bar do
1
else
_ ->
2
end compiles to case _bar@1 of
__value@1 -> 1;
_ -> 2
end. which shows what's already visible in the original example - Just to make sure, I also checked the following: with {:ok, 3} <- bar do
1
else
_ ->
2
end case _bar@1 of
{ok, 3} -> 1;
_ -> 2
end. which, as expected, doesn't generate the warning. @eksperimental thanks for the report! In this case we're doing the right thing, though :) |
with
expression support and clause reachability
Maybe I oversimplified the example. I will try to analyze the real case I had and get back with more info. |
Here's an updated example that fails defmodule Unreacheable do
@spec foo(term) :: {:ok, term} | :error
def foo(bar) do
with pid when is_pid(pid) <- pid_or_nil(bar),
build <- build(bar) do
{:ok, build}
else
_ ->
:error
end
end
@spec pid_or_nil(term) :: pid | nil
def pid_or_nil(_term) do
case Enum.random(1..2) do
1 -> nil
2 -> self()
end
end
@spec build(key) :: {:build, key} when key: term
def build(key) do
{:build, key}
end
end |
This one is even more simplified. defmodule Unreacheable do
require Integer
@spec the_odds(term) :: {:even | :odd, pos_integer()}
def the_odds(bar) do
with integer when Integer.is_odd(integer) <- Enum.random(1..2),
build <- identity(bar) do
{:odd, build}
else
integer ->
{:even, integer}
end
end
def identity(key), do: key
end |
Converting the latter example to generated Erlang code, and back to Elixir, it looks like this. defmodule UnreacheableExpanded do
require Integer
import Bitwise
@spec the_odds(term) :: {:even | :odd, pos_integer()}
def the_odds(bar) do
case Enum.random(1..2) do
integer2 when is_integer(integer2) and band(integer2, 1) == 1 ->
case identity(bar) do
build ->
{:odd, build}
integer1 ->
{:even, integer1}
end
integer1 ->
{:even, integer1}
end
end
def identity(key), do: key
end which generates the following warning on compilation
Which is correct. Then Gradualizer is correct. I wonder if this is considered a bad practice in Elixir. (Ab)using |
The warning will be gone when Elixir supports OTP25+ exclusively as it's planned to port |
@eksperimental I've tried both of the examples (1, 2) and it seems Gradualizer no longer reports an error (I'm not sure why, though).
I think you should use with pid when is_pid(pid) <- pid_or_nil(bar),
build = build(bar) do
{:ok, build}
else
_ ->
:error
end But because it's the last clause, it also makes more sense to it move in the body. We even get a Credo warning for this:
Which gets us here: with pid when is_pid(pid) <- pid_or_nil(bar) do
build = build(bar)
{:ok, build}
else
_ ->
:error
end I think this version is a bit more idiomatic than the original one. |
This is a reduced case of The clause on line X cannot be reached, when calling
with/1
will generate:
The text was updated successfully, but these errors were encountered: