How to define Macro for a new Type?

The value of the macro here would be:

  • it creates an abstraction that hides the implementation detail (using tuples is a detail)
  • it makes the code easier to read and write, by not forcing the user to manually duplicate code everywhere

To further expand, invoking deftype Name, String.t() would generate:

defmodule Name do
  @opaque t :: {:name, String.t()}

  @spec new(val :: String.t()) :: t
  def new(val) when is_binary(val), do: {:name, val}

  @spec extract(name :: t) :: String.t()
  def extract({:name, val}), do: val

  @spec name?(data :: any) :: boolean()
  def name?({:name, val}) when is_binary(val), do: true
  def name?(_data), do: false

  @spec is_name(data :: any) ::
          {:__block__ | {:., [], [:andalso | :erlang, ...]}, [],
           [{:= | {any, any, any}, [], [...]}, ...]}
  defguard is_name(value)
           when is_tuple(value) and elem(value, 0) == :name and is_binary(elem(value, 1))
end

While I could in theory define this set of functions for every tuple, I find it that a macro would help with that, while cutting on manual duplication.

2 Likes