Reputation: 1339
I'm implementing a crash
function in a GenServer
to test the behavior of the supervisor and registry that will manage this process. Work is done in Elixir but I believe it may also concern Erlang.
I could have called raise()
but in the first place I implemented 1/0
as the reason for crash. The compiler being a smart guy, for the following code:
def handle_cast(:crash, state) do
a = 1 / 0
{:noreply, state}
end
I got the warning:
warning: this expression will fail with ArithmeticError
lib/xyz/worker.ex:47
Fair. After all, even old C or C++ compilers were able to detect this kind of things. I tried a library call replacing a = 1 / 0
by a = 1 / :math.sin(0)
. Same warning. My curiosity woke up and I tried different things with the same outcome. Actually, it looks like this is not so easy to fool the compiler! Eventually, I put:
a = 1 / Enum.reduce([0, 1, -1], 0, fn(n, acc) -> n+acc end)
and got a different warning:
warning: the result of the expression is ignored (suppress the warning by assigning the expression to the _ variable)
lib/xyz/worker.ex:50
line 50 being a = 1 / Enum.reduce(...)
.
I spent a couple of hours trying different things with always getting either warning.
I believe the first one is raised because the compiler is able to precalculate the result out of constant arguments and of function type and inline eventually the operation 1 / 0
.
Yet I don't understand the second warning. In one of the tests, I wrote :
def handle_cast(:crash, state) do
a = 1 / Enum.reduce([0, 1, -1], 0, fn(n, acc) -> n+acc end)
# {:noreply, state}
end
which actually suppresses the warning, but I really don't understand why.
NB.1: Versions:
maurice@mickey> elixir -v
Erlang/OTP 20 [erts-9.2] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Elixir 1.6.1 (compiled with OTP 19)
NB.2: I'm aware of this question, yet I don't think the reply applies here.
For the time being, I'll be calling raise(...)
...
Upvotes: 0
Views: 2378
Reputation: 20138
The warning is actually telling you what to do: replace a = ...
with _ = ...
.
In your examples at hand you assign the result of the operation to a variable called a
. The compile notices that you never use that variable again, so it complains about it.
Elixir knows a "special" variable for that case called _
. When doing _ = ...
or def my_function(_, second_paramter)
you basically tell the compiler:
I don't want to use that value, so please don't complain
To provide more information about the ignored value, you can also prefix a variable with an underscore (_
), which serves the same purpose. In your case that could be _a = ...
.
This is mainly useful when ignoring arguments in a function, without leaving the reader to guess what that argument was about. So def get(:thing, _)
could become def get(:thing, _opts)
.
Then you asked why the commented out version didn't produce that error. The answer to that lies in the fact that the return value of a function is equal to the last statement of that function.
So this function
def my_function do
1
2
3
end
returns 3, while this function
def my_function do
:a
:b
end
returns :b
. As such in your example
def handle_cast(:crash, state) do
a = 1 / Enum.reduce([0, 1, -1], 0, fn(n, acc) -> n+acc end)
# {:noreply, state}
end
You commented out the # {:noreply, state}
tuple and the a = ...
statement becomes the last one in the function. Since now you create the variable a
and evaluate it as part of the "return", the compiler stops complaining.
On the other hand, a fair case could be made that a variable assignment in the last line of a function is useless. So this might actually warrant a low priority issue on GitHub.
Upvotes: 4
Reputation: 121000
Fool the compiler till the end, reassign state
variable:
def handle_cast(:crash, state) do
state = Enum.reduce([0, 1, -1], 0, fn(n, acc) ->
n + acc
end)
{:noreply, state}
end
That way the compiler will think state
assignment is necessary (since it’s used as a return value.)
Upvotes: 0