背景
我正在尝试使用透析器进行多态打字。作为一个例子,我正在使用著名的Option
类型(又名 Maybe Monad)现在在许多其他语言中都很流行。
defmodule Test do
@type option(t) :: some(t) | nothing
@type some(t) :: [{:some, t}]
@type nothing :: []
@spec validate_name(String.t()) :: option(String.t())
def validate_name(name) do
if String.length(name) > 0 do
[{:some, name}]
else
nil
end
end
end
正如你所看到的,该函数validate_name
应该返回(根据规范定义)[{:some, String.t}] | []
.
这里的问题是,实际上,该函数正在返回[{:some, String.t}] | nil
. nil
与空列表不同[]
.
Problem
鉴于这个问题,我预计透析器会抱怨。然而它很乐意接受这个错误的规范:
$ mix dialyzer
Compiling 1 file (.ex)
Finding suitable PLTs
Checking PLT...
[:compiler, :currying, :elixir, :gradient, :gradualizer, :kernel, :logger, :stdlib, :syntax_tools]
PLT is up to date!
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.Book.beam',
'/home/user/Workplace/fl4m3/grokking_fp/_build/dev/lib/grokking_fp/ebin/Elixir.DealingWithListsOfLists.beam',
'/home/user/Workplace/fl4m3/grokking_fp/_build/dev/lib/grokking_fp/ebin/Elixir.Event.beam',
'/home/user/Workplace/fl4m3/grokking_fp/_build/dev/lib/grokking_fp/ebin/Elixir.FlatMapsVSForComprehensions.beam',
'/home/user/Workplace/fl4m3/grokking_fp/_build/dev/lib/grokking_fp/ebin/Elixir.ImmutableValues.beam',
...],
warnings: [:unknown]
]
Total errors: 0, Skipped: 0, Unnecessary Skips: 0
done in 0m1.09s
done (passed successfully)
此外,无论我在其中放入什么else
分支,结果始终是“快乐的透析器”。
Question
在这一点上,我能想到的唯一合乎逻辑的解决方案是透析器是only关心幸福之路。意思是,它会忽略我的else
branch.
如果 dialzyer 只关心快乐路径,那么这可以解释这个问题(毕竟它被称为成功输入),但这也意味着它会完全错过我的代码中的一堆错误。
- 我对透析器的假设正确吗?
- 有没有办法让它更精确地发现错误,或者这是透析器使用的算法的限制? (因此无法修复)