为什么透析器没有检测到这种不良类型?

时间:2017-06-04 21:14:14

标签: erlang elixir dialyzer

在这种情况下,Dialyzer对我的行为相当奇怪,而且我还没有找到更好的理解它。

这不是错误:

defmodule Blog.UserResolver do
  @type one_user :: ( {:error, String.t} )

  @spec find(%{id: String.t}, any()) :: one_user

  def find(%{id: id}, _info) do
    age = :rand.uniform(99)
    if (age < 100) do
      # This doesn't trigger a type error, even though it's wrong
      {:ok, %{email: "dw@1g.io", name: "Deedub"}}      
    else 
    {:error, "Age isn't in the right range"}
    end
  end
end

请注意,其中一个可能的返回分支肯定是与类型签名不匹配。

然而这确实有错误:

defmodule Blog.UserResolver do
  @type one_user :: ( {:error, String.t} )

  @spec find(%{id: String.t}, any()) :: one_user

  # Throws an error since no return path matches the type spec
  def find(%{id: id}, _info) do
    age = :rand.uniform(99)
    if (age < 100) do
      {:ok, %{email: "dw@1g.io", name: "Deedub"}}      
    else 
     10
    end
  end
end

在这种情况下,可能的分支的 none 与typespec匹配,而dialyzer说有此错误消息:

web/blog/user_resolver.ex:4: Invalid type specification for function 'Elixir.Blog.UserResolver':find/2. The success typing is (#{'id':=_, _=>_},_) -> 10 | {'ok',#{'email':=<<_:64>>, 'name':=<<_:48>>}}

我不明白的部分是透析器清楚识别分支可能返回的两种不同类型((#{'id':=_, _=>_},_) -> 10 | {'ok',#{'email':=<<_:64>>, 'name':=<<_:48>>}),所以它不是问题推理。那么为什么它不会认识到其中一个分支不符合类型规范(如果只有一个的分支符合,这似乎很高兴,这根本不是我想要的)< / p>

1 个答案:

答案 0 :(得分:6)

在Dogbert提供的LearnYou链接中,dialyzer将:

  

只会抱怨可以保证崩溃的类型错误。

在第一个示例中,如果age始终大于或等于100,则函数将返回声明的类型。在第二个示例中,您的函数无法返回声明的类型。

dialyzer创建一组约束方程。如果对这些方程有任何解决方案,那么透析器就不会抱怨。 Erlang是作为动态类型语言创建的。 dialyzer只是一个人事后写的程序。由于我确信他们在思考和讨论并理论化的原因,透析器的设计者选择了这种功能。

  

如果可能,我正在寻找更严格的类型检查器。

到目前为止不可能:

  

Erlang类型系统

     

没有更复杂的类型系统的原因是没有   Erlang发明者知道如何写一个,所以它永远不会完成。该   静态类型系统的优点是可以预测错误   编译时而不是在运行时,因此允许故障   早先检测到并以较低的成本固定。很多人都有   试图为Erlang构建一个静态类型系统。不幸的是,由于   Erlang发明时所做的设计决策,没有任何项目   能够写一个全面的类型系统,因为有热代码   装载,这本质上是困难的。引用乔阿姆斯特朗的话   许多类型系统之一的火焰战争,“它似乎应该是   '简单' - 事实上,几周的编程可以制作一个类型系统   处理95%的语言。一些人的工作[由一些人   计算机科学中最聪明的人已经尝试着解决问题   另外5% - 但这真的很难。“

来自“Erlang编程(Francesco Cesarini&amp; Simon Thompson)”。

需要test suite来控制动态类型的程序。 Elixir只是Erlang的Rubified版本。 Ruby也是一种动态类型的语言 - 但它没有透析器。 Ruby唯一有的就是测试。您可以使用测试套件来控制计算机编程语言的Wild West,而不是编译器。如果你需要一个静态类型的语言,那么一个Rubified版本的Erlang不是一个很好的选择 - 参见Haskell。

相关问题