如何为Elixir结构验证/实施类型和值?

Til*_*ilo 5 elixir

如何为Elixir结构验证/强制使用值的类型和范围?

例如,在结构创建期间,如果输入了无效的类型/值,则会引发错误

  • 纬度应为数值,且应介于-90.0和+90.0之间
  • lon应该是数值,且应介于-180.0和+180.0之间

defmodule Location do
  @enforce_keys [:lat, :lon]
  defstruct lat: 0, lon: 0
end
Run Code Online (Sandbox Code Playgroud)

这里与@JoséValim进行了一些讨论,但不清楚结果是什么 https://groups.google.com/forum/#!topic/elixir-lang-core/U_wdxEqWj_Y

Ale*_*kin 5

无论你是在寻找终身守卫/类型保证,这是不可能的。结构体是下面的裸图

defmodule Location do
  @enforce_keys [:lat, :lon]
  defstruct lat: 0, lon: 0
end

loc = %Location{lat: 0, lon: 0}
is_map(loc) #? true
Run Code Online (Sandbox Code Playgroud)

甚至更多,一个可以简单地创建一个map__struct__按键的原子,表示该结构的名称,瞧?:

loc_str = %{__struct__: Location, lat: 0, lon: 0}
#? %Location{lat: 0, lon: 0}
Run Code Online (Sandbox Code Playgroud)

或使用Kernel.struct/2不检查任何内容

struct(Location, [lat: 0, lon: 0])
#? %Location{lat: 0, lon: 0}
Run Code Online (Sandbox Code Playgroud)

也就是说,不应将structElixir 类型层次结构中的一等公民视为一等公民。这是一个带有附加字段__struct__集的地图。

在 Elixir 中,我们通常使用Typespecsdialyzer为此目的进行静态代码分析。


She*_*yar 5

就像 @mudasobwa 所说,你不能在 Elixir 中的每一步都做出这些保证,因为它是一种动态类型语言。但是您可以在辅助函数中构建结构时执行此操作。

这是我的一个项目的示例:

defmodule Location do
  defstruct [:latitude, :longitude]

  @moduledoc "A struct representation of geo-coordinates"

  @latitude  %{max: +90,  min: -90}
  @longitude %{max: +180, min: -180}


  @doc "Return a new struct for given lat/longs"
  def new(long, lat) do
    validate_latitude!(lat)
    validate_longitude!(long)

    %Location{latitude: lat, longitude: long}
  end


  # Raise error if latitude is invalid
  defp validate_latitude!(lat) do
    case is_number(lat) && (lat <= @latitude.max) && (lat >= @latitude.min) do
      true -> :ok
      false ->
        raise Location.InvalidData, message: "Invalid value for Latitude"
    end
  end


  # Raise error if longitude is invalid
  defp validate_longitude!(long) do
    case is_number(long) && (long <= @longitude.max) && (long >= @longitude.min) do
      true -> :ok
      false ->
        raise Location.InvalidData, message: "Invalid value for Longitude"
    end
  end

end
Run Code Online (Sandbox Code Playgroud)