Type spec for HashDict

Hi, quite new to elixir and type specs; I can’t seem to get the right way of specifying a hashdict. So, this will not work :frowning: What am I missing? Isn’t a HashDict a Dict and %{} should work? Thanks in advance

defmodule TodoList do
  @moduledoc false
  defstruct auto_id: 1, entries: HashDict.new

  @type todo_list :: %TodoList{auto_id: integer, entries: %{}}

  @spec new :: todo_list
  def new, do: %TodoList{}
end
1 Like

It can be difficult or impossible to specify certain types. Erlang terms (including maps), Elixir’s structs, and
remote type definitions like String.t are about as much as you’ll get. In the past I’ve wanted to specify an Enum.t containing only some specified type, but it can’t be parameterized. Somebody else may know more specifics as to why.

Anyway, I haven’t worked with HashDict or Dict much because they’ve both been deprecated in favor of Map and Keyword lists. Perhaps you’d have better luck with one of them. As shown in the typespecs docs, [key: value_type] specifies a keyword list with keys of type key and values of type value_type, and similarly %{key: value_type} represents maps from type key to type value_type. You can get a bit more fancy, but there’s definitely limitations. The docs give a good overview of what is supported.

2 Likes
  1. HashDict is deprecated.
  2. HashDict.t is defined as @opaque t :: %__MODULE__{size: non_neg_integer, root: term}
  3. %{} in the typelanguage means “the empty map” and a struct can never be an empty map.

So to answer your actual question, you need to specify the type of an HashDict as HashDict.t.

But to go beyond your question, you should not use HashDict at all but use Map, Map.t and %{...} instead.

5 Likes

Thanks @NobbZ ! (also to @jmitchell). You’re both right; the working code is; I added the suggestion by @jmitchell on the comment below:

defmodule TodoList do
  @moduledoc false
  defstruct auto_id: 1, entries: Map.new
  @type todo_list :: %TodoList{auto_id: integer, entries: %{optional(any) => any}}

  @spec new :: todo_list
  def new, do: %TodoList{}
end
2 Likes

Minor suggestion: if you intend to later support inserting items into the entries map, you may want to use %{optional(any) => any} instead of %{}. From the docs:

Notice that the syntactic representation of map() is %{optional(any) => any}, not %{}. The notation %{} specifies the singleton type for the empty map.

2 Likes