Search code examples
formattingelixirelixir-iex

Is there a Switch to enable thousand digit grouping (100_000) by default in Iex


Please is there a switch to enable thousand digit grouping (e.g 100_000) by default in Iex. It would be really helpful if is.

Otherwise how can we specify it in IO.puts?


Solution

  • There is no native option to enable digit grouping as you described according to Inspect.Opts.

    However, the following should work to override the behavior of inspect when using IEx with Integer and Float if you place it in your local ~/.iex.exs file:

    defmodule PrettyNumericInspect do
      def group(value, :binary, true),
        do: value |> group_by(8)
      def group(value, :decimal, true),
        do: value |> group_by(3)
      def group(value, :hex, true),
        do: value |> group_by(2)
      def group(value, :octal, true),
        do: value |> group_by(4)
      def group(value, _, _),
        do: value
    
      defp group_by(value, n) when byte_size(value) > n do
        size = byte_size(value)
        case size |> rem(n) do
          0 ->
            (for << << g :: binary-size(n) >> <- value >>,
              into: [],
              do: g)
            |> Enum.join("_")
          r ->
            {head, tail} = value |> String.split_at(r)
            [head, group_by(tail, n)] |> Enum.join("_")
        end
      end
      defp group_by(value, _),
        do: value
    end
    
    defimpl Inspect, for: Float do
      def inspect(thing, %Inspect.Opts{pretty: pretty}) do
        [head, tail] = IO.iodata_to_binary(:io_lib_format.fwrite_g(thing))
        |> String.split(".", parts: 2)
        [PrettyNumericInspect.group(head, :decimal, pretty), tail]
        |> Enum.join(".")
      end
    end
    
    defimpl Inspect, for: Integer do
      def inspect(thing, %Inspect.Opts{base: base, pretty: pretty}) do
        Integer.to_string(thing, base_to_value(base))
        |> PrettyNumericInspect.group(base, pretty)
        |> prepend_prefix(base)
      end
    
      defp base_to_value(base) do
        case base do
          :binary  -> 2
          :decimal -> 10
          :octal   -> 8
          :hex     -> 16
        end
      end
    
      defp prepend_prefix(value, :decimal), do: value
      defp prepend_prefix(value, base) do
        prefix = case base do
          :binary -> "0b"
          :octal  -> "0o"
          :hex    -> "0x"
        end
        prefix <> value
      end
    end
    

    The Inspect.Opts option :pretty must be set to true for the digit grouping to be displayed. According to the documentation for IEx.configure/1 pretty inspect should be enabled by default.

    When launching iex, you will see 2 warnings about redefining Inspect.Float and Inspect.Integer, but it should continue to work like normal afterwards:

    iex> 100_000
    100_000
    iex> 100_000.1
    100_000.1
    

    It also supports groupings for the different :base options (:binary, :decimal, :octal, and :hex):

    iex> inspect 0b11111111_11111111, base: :binary, pretty: true
    "0b11111111_11111111"
    iex> inspect 999_999, base: :decimal, pretty: true
    "999_999"
    iex> inspect 0o7777_7777, base: :octal, pretty: true
    "0o7777_7777"
    iex> inspect 0xFF_FF, base: :hex, pretty: true
    "0xFF_FF"