2

以下は 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: The variable _@1 can never match since previous clauses completely covered the type #{'__struct__':='Elixir.Foo', _=>_}

私は検索して検索しましたが、これが何を意味するのか、またはそれを修正する方法の説明を見つけることができません。

4

1 に答える 1

9

コードを単純化すると、次のようになります。

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.

caseElixirが慎重に生成したステートメントを見ると、その関数内にあることが保証されている%__MODULE__{foo | x: 5}ため、決して一致しないブランチが含まれていることがわかります。これは、異なる構造体のa で構文を使用すると、Elixir がエラーをスローするため、Elixir によって生成されます。__struct__Foo%Struct{map | ...}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}

結果は同じになり、警告は表示されません。

于 2016-08-06T05:57:19.230 に答える