简体   繁体   English

`changeset()` 规范要求逻辑上不可为空的可空类型

[英]`changeset()` spec requires nullable type which is not logically nullable

I have the following code:我有以下代码:

defmodule Foo do
  @moduledoc false

  use Ecto.Schema

  import Ecto.Changeset


  @type t :: %__MODULE__{
          id: integer(),
          foo: String.t(),
          baz_id: String.t(),
          bar: String.t() | nil
        }

  embedded_schema do
    field :foo, :string
    field :bar, :string
  end

  @spec changeset(t() | Ecto.Changeset.t(), map()) :: Ecto.Changeset.t()
  def changeset(bae \\ %__MODULE__{}, attrs) do
    bae
    |> cast(attrs, @fields)
    |> unique_constraint(:baz_id)
  end
end

foo and baz_id should not be nil , as per the @type definition.根据@type定义, foobaz_id不应为nil However, dialyzer is complaining (with the given @spec ) because the default value %__MODULE__{} will set them to nil .但是, dialyzer抱怨(使用给定的@spec ),因为默认值%__MODULE__{}会将它们设置为nil

If I replace the @type definition with:如果我将@type定义替换为:

...
  @type t :: %__MODULE__{
          id: integer() | nil,
          foo: String.t() | nil,
          baz_id: String.t() | nil,
          bar: String.t() | nil
        }
...

then dialyzer will not complain, but I am no longer capturing the idea that some of the fields are not nullable.那么dialyzer不会抱怨,但我不再认为某些字段不可为空。

What would be an elegant way to make changeset() work the way it currently is, and avoid having dialyzer complaining for this specific use?什么是让changeset()以目前的方式工作并避免dialyzer抱怨这种特定用途的优雅方法?

Well, you're explicitly specifying the default argument that violates a dialyzer contract (the schema is a bare struct underneath without default values,) that's why dialyzer complains.好吧,您明确指定了违反透析器合同的默认参数(架构是下面没有默认值的裸结构),这就是透析器抱怨的原因。

It is unclear, how you do suppose to handle an empty %__MODULE__{} once it's not allowed, but answering the question stated, the workaround would be to accept nil argument as a default.目前尚不清楚,一旦不允许,您应该如何处理空的%__MODULE__{} ,但回答所述问题时,解决方法是接受nil参数作为默认值。

@spec changeset(
    nil | t() | Ecto.Changeset.t(), map()
  ) :: Ecto.Changeset.t()
def changeset(bae \\ nil, attrs) do
  bae
  |> Kernel.||(%__MODULE__{})
  |> cast(attrs, @fields)
  |> unique_constraint(:baz_id)
end

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM