Erlang:将类型规范添加到代码中会使透析器更有效吗?
Erlang: Will adding type spec to code make dialyzer more effective?
我有一个项目在代码中没有-spec
或-type
,目前dialyzer可以找到一些警告,其中大部分是机器生成的代码。
将类型规范添加到代码中会使透析器发现更多错误吗?
题外话,有没有什么工具可以检查是否违反了规范?
添加类型规范将显着提高 Dialyzer 的准确性。
因为 Erlang 是一种动态语言,Dialyzer 必须默认对类型进行相当广泛的解释,除非您给它提示以缩小它将通过的 "success" 类型。把它想象成给 Dialyzer 一个过滤器,通过它可以将一组可能的成功转换为 应该 永远有效的显式类型的子集。
这与 Haskell 不同,其中默认假设是失败,并且所有代码都必须以成功键入的方式编写才能完全编译 -- Dialyzer 必须默认假设成功,除非它确实知道类型将要失败。
Typespecs 是其中的主要部分,但 Dialyzer 也会检查守卫,所以像
这样的函数
increment(A) -> A + 1.
与
不一样
increment(A) when A > 100 -> A + 1.
尽管两者都可以输入为
-spec increment(integer()) -> integer().
大多数时候您只关心 integer()
、pos_integer()
、neg_integer()
或 non_neg_integer()
的整数值,但偶尔您只需要一个有界的任意范围一方面——类型语言目前无法表示这一点(尽管我个人希望看到 100..infinity
的声明按预期工作)。
when A > 100
的无界范围需要保护,但像 when A > 100 and A < 201
这样的有界范围可以单独在类型规范中表示:
-spec increment(101..200) -> pos_integer().
increment(A) -> %stuff.
Guards fast 除了调用 length/1
(你可能永远不需要在 guard 中),所以不用担心性能开销直到你真正知道并且可以证明你有一个来自守卫的性能问题。使用守卫 和 类型规范来约束 Dialyzer 非常有用。它作为您自己的文档也非常有用,特别是如果您使用 edoc,因为类型规范将在那里显示,使 API 不那么神秘并且一目了然容易玩弄。
有一些关于在现有代码库中使用 Dialyzer 的有趣文献。这里有一个有据可查的经验:Gradual Typing of Erlang Programs: A Wrangler Experience. (Unfortunately some of the other links I learned a lot from previously have disappeared or moved. (!.!) A careful read of the Wrangler paper, skimming over the User's Guide and man page,使用 Dialyzer,以及以前在类型系统(如 Haskell 中的一些经验)将不仅仅是让你为从 Dialyzer 中获得更多里程做好准备,尽管.)
[附带一提,我之前和一些人谈过关于指定 "pure" 函数的问题,这些函数可以通过符号或使用不同的定义语法(可能是 Prolog 的 :-
而不是 Erlang 的 ->
... 或其他东西),但尽管那会很酷,而且即使现在也很有可能将副作用集中在程序的一小部分并将所有结果传回在 {Results, SideEffectsTODO}
的元组中,这根本不是迫切需要,Erlang 按原样工作得非常好。但 Dialyzer 确实非常有用,可以告诉您哪里迷路了!]
我有一个项目在代码中没有-spec
或-type
,目前dialyzer可以找到一些警告,其中大部分是机器生成的代码。
将类型规范添加到代码中会使透析器发现更多错误吗?
题外话,有没有什么工具可以检查是否违反了规范?
添加类型规范将显着提高 Dialyzer 的准确性。
因为 Erlang 是一种动态语言,Dialyzer 必须默认对类型进行相当广泛的解释,除非您给它提示以缩小它将通过的 "success" 类型。把它想象成给 Dialyzer 一个过滤器,通过它可以将一组可能的成功转换为 应该 永远有效的显式类型的子集。
这与 Haskell 不同,其中默认假设是失败,并且所有代码都必须以成功键入的方式编写才能完全编译 -- Dialyzer 必须默认假设成功,除非它确实知道类型将要失败。
Typespecs 是其中的主要部分,但 Dialyzer 也会检查守卫,所以像
这样的函数increment(A) -> A + 1.
与
不一样increment(A) when A > 100 -> A + 1.
尽管两者都可以输入为
-spec increment(integer()) -> integer().
大多数时候您只关心 integer()
、pos_integer()
、neg_integer()
或 non_neg_integer()
的整数值,但偶尔您只需要一个有界的任意范围一方面——类型语言目前无法表示这一点(尽管我个人希望看到 100..infinity
的声明按预期工作)。
when A > 100
的无界范围需要保护,但像 when A > 100 and A < 201
这样的有界范围可以单独在类型规范中表示:
-spec increment(101..200) -> pos_integer().
increment(A) -> %stuff.
Guards fast 除了调用 length/1
(你可能永远不需要在 guard 中),所以不用担心性能开销直到你真正知道并且可以证明你有一个来自守卫的性能问题。使用守卫 和 类型规范来约束 Dialyzer 非常有用。它作为您自己的文档也非常有用,特别是如果您使用 edoc,因为类型规范将在那里显示,使 API 不那么神秘并且一目了然容易玩弄。
有一些关于在现有代码库中使用 Dialyzer 的有趣文献。这里有一个有据可查的经验:Gradual Typing of Erlang Programs: A Wrangler Experience. (Unfortunately some of the other links I learned a lot from previously have disappeared or moved. (!.!) A careful read of the Wrangler paper, skimming over the User's Guide and man page,使用 Dialyzer,以及以前在类型系统(如 Haskell 中的一些经验)将不仅仅是让你为从 Dialyzer 中获得更多里程做好准备,尽管.)
[附带一提,我之前和一些人谈过关于指定 "pure" 函数的问题,这些函数可以通过符号或使用不同的定义语法(可能是 Prolog 的 :-
而不是 Erlang 的 ->
... 或其他东西),但尽管那会很酷,而且即使现在也很有可能将副作用集中在程序的一小部分并将所有结果传回在 {Results, SideEffectsTODO}
的元组中,这根本不是迫切需要,Erlang 按原样工作得非常好。但 Dialyzer 确实非常有用,可以告诉您哪里迷路了!]