首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Dialyzer不会捕捉返回函数的错误。

Dialyzer不会捕捉返回函数的错误。
EN

Stack Overflow用户
提问于 2022-02-16 08:44:00
回答 1查看 166关注 0票数 1

背景

在玩透析器,类型测试和跑步的时候,我创造了一个透析器中假阳性的例子。

为了这个米维的目的,我使用二烯丙基 (包括版本),因为它使我的生活更容易。透析器的作者证实,这对他们来说不是一个问题,所以暂时排除了这种可能性。

环境

代码语言:javascript
运行
复制
$ elixir -v
Erlang/OTP 24 [erts-12.2.1] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit]
Elixir 1.13.2 (compiled with Erlang/OTP 24)
  • 你用的是哪种版本的透析器?(cat mix.lock _ grep透析器):
代码语言:javascript
运行
复制
"dialyxir": {:hex, :dialyxir, "1.1.0", "c5aab0d6e71e5522e77beff7ba9e08f8e02bad90dfbeffae60eaf0cb47e29488", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "07ea8e49c45f15264ebe6d5b93799d4dd56a44036cf42d0ad9c960bc266c0b9a"},
"erlex": {:hex, :erlex, "0.2.6", "c7987d15e899c7a2f34f5420d2a2ea0d659682c06ac607572df55a43753aa12e", [:mix], [], "hexpm", "2ed2e25711feb44d52b17d2780eabf998452f6efda104877a3881c2f8c0c0c75"},

当前行为

给定以下代码示例:

代码语言:javascript
运行
复制
defmodule PracticingCurrying do

  @spec greater_than(integer()) :: (integer() -> String.t())
  def greater_than(min) do
    fn number -> number > min end
  end

end

显然输入错误,我得到了一个成功的信息:

代码语言:javascript
运行
复制
$ mix dialyzer
Compiling 1 file (.ex)
Generated grokking_fp app
Finding suitable PLTs
Checking PLT...
[:compiler, :currying, :elixir, :gradient, :gradualizer, :kernel, :logger, :stdlib, :syntax_tools]
Looking up modules in dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt
Finding applications for dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt
Finding modules for dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt
Checking 518 modules in dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt
Adding 44 modules to dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt
done in 0m24.18s
No :ignore_warnings opt specified in mix.exs and default does not exist.

Starting Dialyzer
[
  check_plt: false,
  init_plt: '/home/user/Workplace/fl4m3/grokking_fp/_build/dev/dialyxir_erlang-24.2.1_elixir-1.13.2_deps-dev.plt',
  files: ['/home/user/Workplace/fl4m3/grokking_fp/_build/dev/lib/grokking_fp/ebin/Elixir.ImmutableValues.beam',
   '/home/user/Workplace/fl4m3/grokking_fp/_build/dev/lib/grokking_fp/ebin/Elixir.PracticingCurrying.beam',
   '/home/user/Workplace/fl4m3/grokking_fp/_build/dev/lib/grokking_fp/ebin/Elixir.TipCalculator.beam'],
  warnings: [:unknown]
]
Total errors: 0, Skipped: 0, Unnecessary Skips: 0
done in 0m1.02s
done (passed successfully)

预期行为

我希望透析器告诉我正确的规格是@spec greater_than(integer()) :: (integer() -> bool())

顺便提一句(如果你愿意的话,比较一下),梯度确实会发现错误。我知道比较这些工具就像比较橘子和苹果,但我认为这仍然值得一提。

问题

  1. 透析器不打算捕捉这类错误吗?
  2. 如果它应该捕获错误,什么可能是失败的?(是我的例子不正确,还是透析器里的东西?)

我个人觉得很难相信这可能是一个错误在透析器,这个工具已经被很多人广泛地使用,我是第一个发现这个错误。然而,我无法解释发生了什么。

我们很感激你的帮助。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-02-22 10:06:53

透析器的分析相当乐观,忽略了某些类型的错误。这篇文章提供了一些关于其方法和局限性的高级解释。

在匿名函数的特定情况下,在声明透析器时,它们执行的检查似乎非常少:它将忽略参数的类型和返回类型,例如,以下内容即使明显错误,也不会导致任何错误:

代码语言:javascript
运行
复制
# no error
@spec add(integer()) :: (String.t() -> String.t())
def add(x) do
  fn y -> x + y end
end

然而,它将指出一种不匹配的原则。

代码语言:javascript
运行
复制
# invalid_contract
# The @spec for the function does not match the success typing of the function.

@spec add2(integer()) :: (integer(), integer() -> integer())
def add2(x) do
  fn y -> x + y end
end

在尝试使用匿名函数时,Dialyzer可能能够检测到类型冲突,但这并不一定(见上面的文章),错误消息可能没有帮助:

代码语言:javascript
运行
复制
# Function main/0 has no local return.

def main do
  positive? = greater_than(0)
  positive?.(2)
end

我们不知道到底是什么问题,甚至连引起错误的线也不知道。但是至少我们知道有一个并且可以调试它。

在下面的示例中,错误提供了更多的信息(使用:lists.map/2而不是Enum.map/2,因为透析器不理解可枚举协议):

代码语言:javascript
运行
复制
# Function main2/0 has no local return.

def main2 do
  positive? = greater_than(0)

  # The function call will not succeed.
  # :lists.map(_positive? :: (integer() -> none()), [-2 | 0 | 1, ...])
  # will never return since the success typing arguments are
  # ((_ -> any()), [any()])

  :lists.map(positive?, [1, 0, -2])
end

这告诉我们透析器推断greater_than/1的返回类型为(integer() -> none())。在上述文章中,none被描述为:

这是一种特殊的类型,它意味着没有任何术语或类型是有效的。通常,当透析器将一个函数的可能返回值降到none()时,这意味着该函数应该崩溃。这是“这种东西行不通”的同义词。

因此,透析器知道这个函数不能被成功调用,但在实际调用之前并不认为它是一个类型冲突,因此它将允许声明(就像您可以完美地创建一个只使用raises的函数一样)。

免责声明:我找不到关于透析器如何处理匿名功能的官方解释,所以上面的解释是基于我的观察和解释。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/71138597

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档