为什么透析器检测不到这种不良类型?
Why doesn't dialyzer detect this bad type?
在这种情况下,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
请注意,可能的 return 分支之一 绝对 与类型签名不匹配。
然而这确实有一个错误:
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 可能的分支与类型规范匹配,透析器说有此错误消息:
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>>}}
我不明白的部分是透析器 清楚地 识别分支可能 return ((#{'id':=_, _=>_},_) -> 10 | {'ok',#{'email':=<<_:64>>, 'name':=<<_:48>>}
) 的两种不同类型,所以它不是一个推理问题。那么为什么它不承认其中一个分支不符合类型规范(如果只有一个分支符合,这似乎很高兴,这不是我想要的全部)
根据 Dogbert 提供的 LearnYou link,dialyzer
将:
only complain on type errors that would guarantee a crash.
在您的第一个示例中,如果年龄始终大于或等于 100,您的函数将 return 声明的类型。在第二个示例中,您的函数无法 return 声明的类型。
dialyzer
创建一组约束方程。如果这些方程有任何解,那么透析器就不会报错。 Erlang 是作为动态类型语言创建的。 dialyzer
只是某人事后编写的程序。出于我确信他们思考、讨论和理论化的原因,透析器的设计者选择了该功能。
I'm looking for a more rigorous type checker if possible.
目前还不可能:
The Erlang Type System
The reason for not having a more elaborate type system is that none of
the Erlang inventors knew how to write one, so it never got done. The
advantage of a static type system is that errors can be predicted at
compile time rather than at runtime, therefore allowing faults to be
detected earlier and fixed at a lower cost. A number of people have
tried to build a static type system for Erlang. Unfortunately, due to
design decisions taken when Erlang was invented, no project has been
able to write a comprehensive type system, since with hot code
loading, this is intrinsically difficult. To quote Joe Armstrong in
one of the many type system flame wars, "It seems like it should be
'easy'—and indeed, a few weeks programming can make a type system that
handles 95% of the language. Several man-years of work [by some of the
brightest minds in computer science] have gone into trying to fix up
the other 5%—but this is really difficult."
来自 "Erlang Programming (Francesco Cesarini & Simon Thompson)".
需要 test suite
来控制动态类型的程序。 Elixir 只是 Erlang 的 Rubified 版本。 Ruby 也是一种动态类型语言——但它没有透析器。 Ruby 唯一拥有的就是测试。您使用测试套件来控制计算机编程语言的狂野西部——而不是编译器。如果您需要静态类型的语言,那么 Erlang 的 Rubified 版本不是一个很好的选择——参见 Haskell。
在这种情况下,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
请注意,可能的 return 分支之一 绝对 与类型签名不匹配。
然而这确实有一个错误:
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 可能的分支与类型规范匹配,透析器说有此错误消息:
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>>}}
我不明白的部分是透析器 清楚地 识别分支可能 return ((#{'id':=_, _=>_},_) -> 10 | {'ok',#{'email':=<<_:64>>, 'name':=<<_:48>>}
) 的两种不同类型,所以它不是一个推理问题。那么为什么它不承认其中一个分支不符合类型规范(如果只有一个分支符合,这似乎很高兴,这不是我想要的全部)
根据 Dogbert 提供的 LearnYou link,dialyzer
将:
only complain on type errors that would guarantee a crash.
在您的第一个示例中,如果年龄始终大于或等于 100,您的函数将 return 声明的类型。在第二个示例中,您的函数无法 return 声明的类型。
dialyzer
创建一组约束方程。如果这些方程有任何解,那么透析器就不会报错。 Erlang 是作为动态类型语言创建的。 dialyzer
只是某人事后编写的程序。出于我确信他们思考、讨论和理论化的原因,透析器的设计者选择了该功能。
I'm looking for a more rigorous type checker if possible.
目前还不可能:
The Erlang Type System
The reason for not having a more elaborate type system is that none of the Erlang inventors knew how to write one, so it never got done. The advantage of a static type system is that errors can be predicted at compile time rather than at runtime, therefore allowing faults to be detected earlier and fixed at a lower cost. A number of people have tried to build a static type system for Erlang. Unfortunately, due to design decisions taken when Erlang was invented, no project has been able to write a comprehensive type system, since with hot code loading, this is intrinsically difficult. To quote Joe Armstrong in one of the many type system flame wars, "It seems like it should be 'easy'—and indeed, a few weeks programming can make a type system that handles 95% of the language. Several man-years of work [by some of the brightest minds in computer science] have gone into trying to fix up the other 5%—but this is really difficult."
来自 "Erlang Programming (Francesco Cesarini & Simon Thompson)".
需要 test suite
来控制动态类型的程序。 Elixir 只是 Erlang 的 Rubified 版本。 Ruby 也是一种动态类型语言——但它没有透析器。 Ruby 唯一拥有的就是测试。您使用测试套件来控制计算机编程语言的狂野西部——而不是编译器。如果您需要静态类型的语言,那么 Erlang 的 Rubified 版本不是一个很好的选择——参见 Haskell。