Replacing static key-value lookup with Macros

I’m building part of a reporting system. Specific fields are reported as either integer, 2-digit or 4-digit decimals.

There is a large lookup map (over 600 items) that defines how each field is to be reported:

%{“report_field-name” => decimal_places} like below:

%{
"AWBR1000_amount-payable" => 4,
"AWBR1000_amount-received" => 2,
"AWBR100_accrued-right" => 2,
"AWBR100_annual-total-emolument" => 2,
"AWBR100_monthly-programed-drawndown" => 2,
"AWBR100_recommended-lumpsum" => 2,
"AWBR100_retirement-age" => 0,
"AWBR100_rsa-balance" => 2,
"AWBR101_annual-total-emolument-adjusted" => 2,
"AWBR101_basic-salary" => 2,
"AWBR101_consolidated-allowance" => 2,
"AWBR101_consolidated-salary" => 2,
"AWBR101_housing-rent" => 2,
"AWBR101_monthly-total" => 2,
"AWBR101_transport" => 2,
"AWBR101_utility" => 2,
"AWBR200_amount-recieved-nsitf-to-rsa" => 2,
"AWBR200_amount-requested-under-nsitf-from-rsa" => 2,
"AWBR200_initial-amount-paid-under-pra" => 2,
"AWBR300_enbloc-payment" => 2,
"AWBR400_enbloc-payment" => 2,
"AWBR400_rsa-balance" => 2
}

Presently, i’m loading this map as a module variable from a config file, like so:
(This formats.exs file will hardly change …)

defmodule RMS.Reports do
  {formats, _} = Code.eval_file("config/formats.exs") # where formats.exs holds all the formats
  @formats formats

How can I use macros to convert this static lookup map into lookup functions, like this:

defp format_val("AWBR1000_amount-payable", nil), do: "0.0000"
defp format_val("AWBR1000_amount-payable", v), do: :erlang.float_to_binary(Decimal.to_float(v), [decimals: 4])
defp format_val("AWBR1000_amount-received", nil), do: "0.00"
defp format_val("AWBR1000_amount-received", v), do: :erlang.float_to_binary(Decimal.to_float(v), [decimals: 2])
defp format_val("AWBR100_retirement-age", nil), do: "0"
defp format_val("AWBR100_retirement-age", v), do: v # when the decimals are zero, its an integer

This video explains the advantages of replacing static look-ups with Macros:

Thanks.

Could be something like (typed entirely in-post here, untested):

defmodule RMS.Reports do
  {formats, _} = Code.eval_file("config/formats.exs")

  for {key, places} <- formats do
    defp format_val(unquote(key), nil), do: unquote(Enum.join(["0", String.duplicate("0", places)], "."))
    defp format_val(unquote(key), v), do: unquote(case places do
      0 -> Macro.var(:v, nil)
      p -> quote do :erlang.float_to_binary(Decimal.to_float(unquote(Macro.var(:v, nil))), [decimals: unquote(p)]) end
    end)
  end

  # ...
end

Or something like that.

1 Like

See my attempt:

defmodule RMS.Reports do
  {formats, _} = Code.eval_file("config/formats.exs")
      for {rpt, deci} <- formats do
        case deci do
          0 ->
            defp format_val(unquote(rpt), nil), do: "0"
            defp format_val(unquote(rpt), v), do: v
          2 ->
            defp format_val(unquote(rpt), nil), do: "0.00"
            defp format_val(unquote(rpt), v), do: :erlang.float_to_binary(Decimal.to_float(Decimal.new(v)), [decimals: 2])
            defp format_val(unquote(rpt), v), do: :erlang.float_to_binary(Decimal.to_float(Decimal.new(v)), [decimals: 2])
          4 ->
            defp format_val(unquote(rpt), nil), do: "0.0000"
            defp format_val(unquote(rpt), v), do: :erlang.float_to_binary(Decimal.to_float(Decimal.new(v)), [decimals: 4])
        end
      end

# ...
end
1 Like