types - 结构的透析器错误

这是 Elixir 1.3 中的一个最小错误示例:

defmodule Foo do
  @type t :: %__MODULE__{x: non_neg_integer}
  defstruct x: 0

  @spec test(t) :: t
  def test(%__MODULE__{} = foo), do: test2(foo)

  @spec test2(t) :: t
  defp test2(%__MODULE__{} = foo), do: %__MODULE__{foo | x: 5}

end

这无法通过以下方式进行类型检查:foo.ex:9: 变量 _@1 永远无法匹配,因为前面的子句完全覆盖了类型 #{'__struct__':='Elixir.Foo', _=> _}.

我搜索了又搜索,终究找不到这意味着什么或如何修复它的解释。

最佳答案

如果将代码简化为:

defmodule Foo do
  @type t :: %__MODULE__{x: non_neg_integer}
  defstruct x: 0

  @spec set_x_to_5(t) :: t
  def set_x_to_5(%__MODULE__{} = foo), do: %__MODULE__{foo | x: 5}
end

然后反编译生成的 .beam 文件,你会得到:

set_x_to_5(#{'__struct__' := 'Elixir.Foo'} = foo@1) ->
    case foo@1 of
      _@1 = #{'__struct__' := 'Elixir.Foo'} -> _@1#{x := 5};
      _@1 -> erlang:error({badstruct, 'Elixir.Foo', _@1})
    end.

如果你看到 Elixir 为 %__MODULE__{foo | 生成的 case 语句x: 5} 仔细看,您会发现它包含一个永远无法匹配的分支,因为 __struct__ 在该函数内保证为 Foo。这是由 Elixir 生成的,因为如果您使用 %Struct{map | Elixir 会抛出错误...} 具有不同结构的 map 的语法:

iex(1)> defmodule Foo, do: defstruct [:x]
iex(2)> defmodule Bar, do: defstruct [:x]
iex(3)> %Foo{%Bar{x: 1} | x: 2}
** (BadStructError) expected a struct named Foo, got: %Bar{x: 1}

要解决此问题,您可以删除 __MODULE__ 部分并执行以下操作:

%{foo | x: 5}

结果是一样的,你不会收到任何警告。

https://stackoverflow.com/questions/38800459/

相关文章:

c# - 如何在 NLog.Config 中使用来自不同类的变量?

javascript - 一个语句中的多个问号 "?"和冒号 ":"如何在 javascript 中

cmake - 从包含的 CMakeList.txt 中删除消息

python - 删除元组列表中的括号

entity-framework - 如何更改 EntityFrameworkCore 迁移的存储位

sql - IPython SQL Magic - 以编程方式生成查询字符串

erlang - 了解透析器结果

regex - 向量化模式匹配返回 R 中的模式

c# - 计算两个日期之间的天数并将其显示在标签中

scala - 如何使用 Avro 序列化 Scala 案例类?