Override display format of lists with integers and floating point numbers

Often when dealing with financial code, you will have something like

mavg = [48, 49]
price = [0.0005, 0.000412]

This gets formatted (if printed to file or logged to console) into

'01'
[0.0005, 4.12e-4]

Makes the data really hard to analyse. Is there any way to pass global directives to never display lists for example as ‘erlang string’ nor use e notation for floats?

Hey @vans163, floats should never, ever be used for currency. They do not retain precision, and math with them can lead to all kinds of unintuitive results. I would at least use Decimal, and perhaps one of the dedicated money libraries.

4 Likes
iex(13)> inspect([48, 49], charlists: :as_lists)
"[48, 49]"

and +1 on what @benwilson512 said…

4 Likes

No one is using floats for currency here… This is just data based off analysis, being off by more than a few units is totally fine.

We don’t really care if the drop chance of a rare item is 0.001412 vs 0.001413, but to state again, we are not using floats for any drop chance. The floats are used for purely analysis.

It seems so annoying to deal with that even turning the float into a string, it retains the e notation.

2 Likes

This is interesting, I followed it to overriding the inspect module here https://github.com/elixir-lang/elixir/blob/fc3104ca98e222103c6e0fa22d112bca4d8049ea/lib/elixir/lib/inspect.ex, and hardcoding to use :as_lists (as well as changing the float formatting function).

Floats perfect, they even format in the REPL! Lists seem to have broken dialyzer; and I see the problem now because its impossible to tell Erlang using erlang strings (lists) apart from lists appearing as strings.

1 Like

You can also configure IEx if you want the results from the REPL to look right:

iex> IEx.configure(inspect: [charlists: :as_lists])
:ok
iex> 'abc'
[97, 98, 99]
4 Likes

Wow nice, is there a way to set this in a config file / have to execute upon running the REPL?

in .iex.exs https://hexdocs.pm/iex/IEx.html#module-the-iex-exs-file

3 Likes

Thank you all I am very satisfied with this until further problems come up.

in .iex.exs add

#Inspect overrides
import Kernel, except: [inspect: 1]
import Inspect.Algebra
alias Code.Identifier

#This makes [48, 49] print as [48, 49]
#IEx.configure(inspect: [charlists: :as_lists])
defimpl Inspect, for: List do
  def inspect([], opts) do
    color("[]", :list, opts)
  end

  def inspect(term, opts) do
    %Inspect.Opts{
      charlists: lists,
      printable_limit: printable_limit
    } = opts

    open = color("[", :list, opts)
    sep = color(",", :list, opts)
    close = color("]", :list, opts)

    cond do
      lists == :as_charlists ->
        inspected =
          case Identifier.escape(IO.chardata_to_string(term), ?', printable_limit) do
            {escaped, ""} -> [?', escaped, ?']
            {escaped, _} -> [?', escaped, ?', " ++ ..."]
          end

        IO.iodata_to_binary(inspected)

      keyword?(term) ->
        container_doc(open, term, close, opts, &keyword/2, separator: sep, break: :strict)

      true ->
        container_doc(open, term, close, opts, &to_doc/2, separator: sep)
    end
  end

  @doc false
  def keyword({key, value}, opts) do
    key = color(Identifier.inspect_as_key(key), :atom, opts)
    concat(key, concat(" ", to_doc(value, opts)))
  end

  @doc false
  def keyword?([{key, _value} | rest]) when is_atom(key) do
    case Atom.to_charlist(key) do
      'Elixir.' ++ _ -> false
      _ -> keyword?(rest)
    end
  end

  def keyword?([]), do: true
  def keyword?(_other), do: false
end

#This makes 0.000412 print as 0.000412
defimpl Inspect, for: Float do
  def inspect(term, opts) do
    inspected = :erlang.float_to_binary(term, [:compact, { :decimals, 7 }])
    color(inspected, :number, opts)
  end
end

This doesnt break dialyzer upon compile unlike when I added this as a module to the project. This probably will need some work (maybe using Consolidation under Protocols) to work with releases or test environment. But works well for my analysis use case.

1 Like