Let's imagine a module implementation Scaffoldthat generates a structure with predefined custom fields and injects it into the called module using use Scaffold. When called use Scaffold, fields: foo: [custom_type()], ... , we want to implement the correct type in the Consumermodule ( common_fieldin the example below, it is defined in Scaffoldor elsewhere from the outside).
@type t :: %Consumer{
  common_field: [atom()],
  foo: [custom_type()],
  ...
}
      It would be cool if we could accurately generate the type Consumer.t()for future use and create appropriate documentation for users of our new module.

A more complicated example would look like this:
defmodule Scaffold do
  defmacro __using__(opts) do
    quote do
      @fields unquote(opts[:fields])
      @type t :: %__MODULE__{
        version: atom()
        # magic
      }
      defstruct @fields
    end
  end
end
defmodule Consumer do
  use Scaffold, fields: [foo: integer(), bar: binary()]
end
      and, after compilation:
defmodule Consumer do
  @type t :: %Consumer{
    version: atom(),
    foo: integer(),
    bar: binary()
  }
  defstruct ~w|version foo bar|a
end
      Looks easy, right?
Naive approach
Let's start by analyzing what AST we get in Scaffold.__using__/1.
  defmacro __using__(opts) do
    IO.inspect(opts)
  end
#โ [fields: [foo: {:integer, [line: 2], []},
#            bar: {:binary, [line: 2], []}]]
      Excellent. It looks as if we are one step away from success.
  quote do
    custom_types = unquote(opts[:fields])
    ...
  end
#โ == Compilation error in file lib/consumer.ex ==
#  ** (CompileError) lib/consumer.ex:2: undefined function integer/0
      Bams! Types are something special, as they say in the Privoz area; we can't just take and get them from the AST anywhere. Maybe it unquotewill work locally?
      @type t :: %__MODULE__{
              unquote_splicing([{:version, atom()} | opts[:fields]])
            }
#โ == Compilation error in file lib/scaffold.ex ==
#  ** (CompileError) lib/scaffold.ex:11: undefined function atom/0
      No matter how it is. Types are tiresome; ask anyone who makes a living with a Haskell (and these are the types of smoker in Haskell; real - dependent - types are a hundred times more useful, but two hundred times more difficult).
, , AST , , .
AST
       , ,     .  ,       , ,   -      .  ,    .            ,  ,      AST  (    unquote   binary()             ,   CompileError.
 ,        quote do,     ,   quote,     โ AST.
quote do
  Enum.map([:foo, :bar], & &1)
end
#โ {
#   {:., [], [{:__aliases__, [alias: false], [:Enum]}, :map]}, [],
#     [[:foo, :bar], {:&, [], [{:&, [], [1]}]}]}
      ?   ,     AST,   Enum, :map,    .  ,    AST     quote      .  .
, AST AST, . ? โ , .
defmacro __using__(opts) do
  fields = opts[:fields]
  keys = Keyword.keys(fields)
  type = ???
  quote location: :keep do
    @type t :: unquote(type)
    defstruct unquote(keys)
  end
end
      , , โ AST, . , ruby !
iex|1  quote do
...|1    %Foo{version: atom(), foo: binary()}
...|1  end
#โ {:%, [],
#   [
#     {:__aliases__, [alias: false], [:Foo]},
#     {:%{}, [], [version: {:atom, [], []}, foo: {:binary, [], []}]}
#   ]}
      ?
iex|2  quote do
...|2    %{__struct__: Foo, version: atom(), foo: binary()}
...|2  end
#โ {:%{}, [],
#   [
#     __struct__: {:__aliases__, [alias: false], [:Foo]},
#     version: {:atom, [], []},
#     foo: {:binary, [], []}
#   ]}
      , , . .
defmacro __using__(opts) do
  fields = opts[:fields]
  keys = Keyword.keys(fields)
  type =
    {:%{}, [],
      [
        {:__struct__, {:__MODULE__, [], ruby}},
        {:version, {:atom, [], []}}
        | fields
      ]}
  quote location: :keep do
    @type t :: unquote(type)
    defstruct unquote(keys)
  end
end
      ,        Scaffold,   (    : Qqwy here). ,       , version: atom()    quote  .
defmacro __using__(opts) do
  fields = opts[:fields]
  keys = Keyword.keys(fields)
  fields_with_struct_name = [__struct__: __CALLER__.module] ++ fields
  quote location: :keep do
    @type t :: %{unquote_splicing(fields_with_struct)}
    defstruct unquote(keys)
  end
end
                (mix docs):

: AST
 ,        AST   __using__/1 ,     ?   ,         unquote  quote?      ,       ,     .   ,   .
NB ,atom(), , ,GenServer.on_start(). .
,       quote do,       - atom() (   CompileError,    ). , -  :
keys = Keyword.keys(fields)
type =
  {:%{}, [],
    [
      {:__struct__, {:__MODULE__, [], ruby}},
      {:version, {:atom, [], []}}
      | Enum.zip(keys, Stream.cycle([{:atom, [], []}]))
    ]}
        ,         @type?          Quoted Fragment,           :
defmodule Squares do
  Enum.each(1..42, fn i ->
    def unquote(:"squared_#{i}")(),
      do: unquote(i) * unquote(i)
  end)
end
Squares.squared_5
#โ 25
      Quoted Fragments      quote,     (bind_quoted:).  .
defmacro __using__(opts) do
  keys = Keyword.keys(opts[:fields])
  quote location: :keep, bind_quoted: [keys: keys] do
    type =
      {:%{}, [],
        [
          {:__struct__, {:__MODULE__, [], ruby}},
          {:version, {:atom, [], []}}
          | Enum.zip(keys, Stream.cycle([{:atom, [], []}]))
        ]}
    #          โโโโโโโโโโโโโ
    @type t :: unquote(type)
    defstruct keys
  end
end
        unquote/1  ,   bind_quoted:         quote/2.
!