Let's see some use cases. First we configure IEx to use our custom
function:
iex> IEx.configure(inspect: [inspect_fun: &Pretty.inspect/2])
:ok
1. Pretty-printing integers
iex> :math.pow(2, 32) |> trunc()
4_294_967_296
2. Inspect tuples as records
iex> {:foo, :bar}
{:foo, :bar}
iex> {:person, "Alice", 30}
#person(name: "Alice", age: 30)
3. Overwrite struct's Inspect implementation
iex> URI.parse("https://elixir-lang.org")
#URI<https://elixir-lang.org>
---
Code for the `Pretty.inspect/2` function:
defmodule Pretty do
def inspect(integer, opts) when is_integer(integer) do
PrettyIntegers.inspect(integer, opts)
end
def inspect(tuple, opts) when is_tuple(tuple) do
records = %{
{:person, 2} => [:name, :age]
}
PrettyTuples.inspect(tuple, records, opts)
end
def inspect(%URI{} = uri, _opts) do
Inspect.Algebra.concat(["#URI<", URI.to_string(uri), ">"])
end
def inspect(term, opts) do
Inspect.inspect(term, opts)
end
end
defmodule PrettyIntegers do
def inspect(term, %Inspect.Opts{base: :decimal}) do
pretty_decimal(term, "_")
end
def inspect(term, %Inspect.Opts{base: base}) do
Integer.to_string(term, base_to_value(base))
|> prepend_prefix(base)
end
def pretty_decimal(n, separator) when n < 0 do
"-" <> pretty_decimal(abs(n), separator)
end
def pretty_decimal(n, separator) when n >= 1000 do
left = div(n, 1_000)
right = rem(n, 1_000)
right_str = :io_lib.format("~3..0B", [right]) |> IO.iodata_to_binary
pretty_decimal(left, separator) <> separator <> right_str
end
def pretty_decimal(n, _) do
Integer.to_string(n)
end
defp base_to_value(base) do
case base do
:binary -> 2
:octal -> 8
:hex -> 16
end
end
defp prepend_prefix(value, base) do
prefix = case base do
:binary -> "0b"
:octal -> "0o"
:hex -> "0x"
end
prefix <> value
end
end
defmodule PrettyTuples do
import Inspect.Algebra
def inspect({}, _records, opts), do: inspect_tuple([], opts)
def inspect(tuple, records, opts) do
list = Tuple.to_list(tuple)
[record_name | values] = list
case record_fields(records, record_name, tuple) do
{:ok, fields} ->
inspect_record(record_name, fields, values, opts)
_ ->
inspect_tuple(list, opts)
end
end
defp record_fields(records, record_name, tuple) do
fields_size = tuple_size(tuple) - 1
Map.fetch(records, {record_name, fields_size})
end
defp inspect_tuple(list, opts) do
inspect("{", list, "}", &to_doc/2, :flex, opts)
end
defp inspect_record(record_name, fields, values, opts) do
kwlist = Enum.zip(fields, values)
inspect("##{record_name}(", kwlist, ")", &Inspect.List.keyword/2, :strict, opts)
end
defp inspect(open, list, close, fun, break, opts) do
open = color(open, :tuple, opts)
sep = color(",", :tuple, opts)
close = color(close, :tuple, opts)
container_opts = [separator: sep, break: break]
container_doc(open, list, close, opts, fun, container_opts)
end
end