Erlang 长生不老药:编译警告“;表达式的结果被忽略";

Erlang 长生不老药:编译警告“;表达式的结果被忽略";,erlang,elixir,Erlang,Elixir,我正在GenServer中实现一个crash函数,以测试将管理此过程的主管和注册表的行为。这项工作是用长生不老药完成的,但我相信它可能也与二郎有关 我本可以调用raise(),但首先我实现了1/0,作为崩溃的原因。编译器是一个聪明人,用于以下代码: def handle_cast(:crash, state) do a = 1 / 0 {:noreply, state} end 我得到警告: warning: this expression will fail with Ari

我正在
GenServer
中实现一个
crash
函数,以测试将管理此过程的主管和注册表的行为。这项工作是用长生不老药完成的,但我相信它可能也与二郎有关

我本可以调用
raise()
,但首先我实现了
1/0
,作为崩溃的原因。编译器是一个聪明人,用于以下代码:

def handle_cast(:crash, state) do
  a = 1 / 0
  {:noreply, state}    
end
我得到警告:

warning: this expression will fail with ArithmeticError
  lib/xyz/worker.ex:47
warning: the result of the expression is ignored (suppress the warning by assigning the expression to the _ variable)
  lib/xyz/worker.ex:50
公平。毕竟,即使是旧的C或C++编译器也能检测到这种情况。我尝试了一个库调用,用
a=1/:math.sin(0)
替换
a=1/0
。同样的警告。我的好奇心觉醒了,我尝试了不同的事情,结果都是一样的。事实上,这看起来并不是那么容易愚弄编译器!最后,我提出:

a = 1 / Enum.reduce([0, 1, -1], 0, fn(n, acc) -> n+acc end)
得到了不同的警告:

warning: the result of the expression is ignored (suppress the warning by assigning the expression to the _ variable)
  lib/xyz/worker.ex:50
第50行是
a=1/Enum.reduce(…)

我花了几个小时尝试不同的事情,总是得到警告

我认为提出第一个是因为编译器能够预先计算出常量参数和函数类型的结果,并最终内联操作
1/0

但我不明白第二个警告。在其中一次测试中,我写道:

def handle_cast(:crash, state) do
  a = 1 / Enum.reduce([0, 1, -1], 0, fn(n, acc) -> n+acc end)
  # {:noreply, state}    
end
这实际上抑制了警告,但我真的不明白为什么

NB.1:版本:

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:我知道这一点,但我认为答案不适用于这里


目前,我将调用
raise(…)

愚弄编译器,直到最后,重新分配
state
变量:

def handle_cast(:崩溃,状态)do
state=Enum.reduce([0,1,-1],0,fn(n,acc)->
n+acc
(完)
{:诺雷普利州}
结束

这样编译器就会认为有必要分配
state
赋值(因为它被用作返回值。)

愚弄编译器直到结束,重新分配
state
变量:

def handle_cast(:崩溃,状态)do
state=Enum.reduce([0,1,-1],0,fn(n,acc)->
n+acc
(完)
{:诺雷普利州}
结束
这样,编译器会认为赋值是必要的(因为它被用作返回值)。

Short版本 该警告实际上是在告诉您该怎么做:将
a=…
替换为
。=…

长版本 在您手头的示例中,您将操作结果分配给名为
a
的变量。编译程序注意到您不再使用该变量,因此它会对此进行抱怨

Elixir知道这种情况下的一个“特殊”变量,称为
。在执行
.=…
定义my\u函数(\uu,第二个参数)
时,您基本上会告诉编译器:

我不想使用那个值,所以请不要抱怨

要提供有关忽略值的更多信息,还可以在变量前面加下划线(
\uu
),这也有同样的作用。在您的情况下,可能是
\u a=…

这在忽略函数中的参数时非常有用,而不会让读者猜测该参数是关于什么的。所以
def-get(:thing,u)
可以变成
def-get(:thing,opts)


然后你问为什么注释掉的版本没有产生那个错误。答案在于函数的返回值等于该函数的最后一条语句

所以这个函数

def my_function do
  1
  2
  3
end
def my_function do
  :a
  :b
end
返回3,而此函数

def my_function do
  1
  2
  3
end
def my_function do
  :a
  :b
end
返回
:b
。在你的例子中也是如此

def handle_cast(:crash, state) do
  a = 1 / Enum.reduce([0, 1, -1], 0, fn(n, acc) -> n+acc end)
  # {:noreply, state}    
end
您注释掉了
{:noreply,state}
元组,
a=…
语句成为函数中的最后一个语句。由于现在您创建了变量
a
,并将其作为“return”的一部分进行计算,编译器停止抱怨

另一方面,一个公平的例子是,函数最后一行的变量赋值是无用的。因此,这实际上可能会导致在短版本上出现低优先级问题。

该警告实际上是在告诉您该怎么做:将
a=…
替换为
。=…

长版本 在您手头的示例中,您将操作结果分配给名为
a
的变量。编译程序注意到您不再使用该变量,因此它会对此进行抱怨

Elixir知道这种情况下的一个“特殊”变量,称为
。在执行
.=…
定义my\u函数(\uu,第二个参数)
时,您基本上会告诉编译器:

我不想使用那个值,所以请不要抱怨

要提供有关忽略值的更多信息,还可以在变量前面加下划线(
\uu
),这也有同样的作用。在您的情况下,可能是
\u a=…

这在忽略函数中的参数时非常有用,而不会让读者猜测该参数是关于什么的。所以
def-get(:thing,u)
可以变成
def-get(:thing,opts)


然后你问为什么注释掉的版本没有产生那个错误。答案在于函数的返回值等于该函数的最后一条语句

所以这个函数

def my_function do
  1
  2
  3
end
def my_function do
  :a
  :b
end
返回3,而此函数

def my_function do
  1
  2
  3
end
def my_function do
  :a
  :b
end
返回
:b
。在你的例子中也是如此

def handle_cast(:crash, state) do
  a = 1 / Enum.reduce([0, 1, -1], 0, fn(n, acc) -> n+acc end)
  # {:noreply, state}    
end
您注释掉了
{:noreply,state}
元组,
a=…
语句成为函数中的最后一个语句。由于现在您创建了变量
a
,并将其作为“return”的一部分进行计算,编译器停止抱怨


另一方面,一个公平的例子是,函数最后一行的变量赋值是无用的。因此,这实际上可能会导致上出现低优先级问题。

这会生成两个警告:
忽略表达式的结果(通过ass抑制警告)