mszmurlo
mszmurlo

Reputation: 1339

Elixir: compile warning "the result of the expression is ignored "

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

Answers (2)

Alex Wolf
Alex Wolf

Reputation: 20138

Short version

The warning is actually telling you what to do: replace a = ... with _ = ....

Long version

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

Aleksei Matiushkin
Aleksei Matiushkin

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

Related Questions