Elixir:为地图指定” key_type”,其中” key_type”是枚举类型

Elixir: Specifying `key_type` for a map, where `key_type` is an enumerated type

我有一个函数返回一个地图,我想为其定义自定义类型。

执行此操作的过程非常简单,除了从以下文档中处理key_type时:

%{required(key_type) => value_type} # map with required pairs of key_type and value_type

对于我正在定义的这种新类型,key_type应该是表示当前年份或将来年份的任何字符串。

"2019""2025这样的键是好的,但是像"2008"这样的键是不好的。

我希望这可以工作:

1
2
3
4
5
6
7
8
  @type home_sharing_rate :: number
  @type home_sharing_days :: [number]
  @type home_sharing_booking :: { home_sharing_days, home_sharing_rate }

  @type income_year ::"2019" |"2020" |"2021" |"2022" |"2023"
  @type income_month ::"Jan" |"Feb" |"Mar" |"Apr" |"May" |"Jun" |"Jul" |"Aug" |"Sep" |"Oct" |"Nov" |"Dec"

  @type earned_home_sharing_income_summary :: %{ required(income_year) => %{ required(income_month ) => [home_sharing_booking] } }

但没有这种运气。但是,我看到Elixir有一些内置类型,例如non_neg_integer(),这些使我相信我可以将类型定义为枚举,但是现在我不太有什么办法可以做到吗?

TypeScript有很多工具可以实现这一目标,所以我很希望Elixir也能做到。在我看来,不能将类型定义为其值的枚举似乎很奇怪,但是也许Elixir打算使用类型的方式与Typescript不同,而我只是缺少了一些东西。


Typespec仅处理用<<>>语法指定的二进制文件,不幸的是,仅支持大小和单位。这段代码将产生更具描述性的错误消息:

1
2
3
4
defmodule Foo do
  @typep income_year :: <<"2019"::binary-size(4)>> | <<"2020"::binary-size(4)>>
  @type earned :: %{ required(income_year) => any() }
end

产生

1
2
3
** (CompileError) iex:4: invalid binary specification,
  expected <<_::size>>, <<_::_*unit>>, or <<_::size, _::_*unit>>
  with size and unit being non-negative integers

也就是说,您最多只能求助于@typep income_year :: <<_::binary-size(4)>>

无论您是否希望使用这种类型,我都建议使用防护而不是类型:

1
2
3
4
5
6
7
8
9
@allowed_years ~w|2019 2020|
Enum.each(@allowed_years, fn year ->
  def do_something(%{unquote(year) => value} = earned) do
    ...
  end
end)

# sink-all clause
def do_something(_), do: raise"Year is not allowed."