(FunctionClauseError) no function clause matching in anonymous fn/1

Nothing special here. You could use atoms for currency names, but that makes things more complicated when interacting with other systems.

all_currencies = ["AUD", "CVE", "USD"]

I’ve bolded the key words that suggest the data structure, a list of maps:

currencies = [
    %{name: "USD", exchange_rate: 1.23, value: 0.87},
    %{name: "CVE", exchange_rate: 1.75, value: 0.75},
    %{name: "AUD", exchange_rate: 1.01, value: 0.99}
  ]

The numbers here are written as floats, but that may not be what you want. Consider the Decimal library for doing financial arithmetic.

“Record” could also be satisfied with a defstruct (or an Erlang record if you’re feeling exotic).

Looking up a record is spelled Enum.find(currencies, & &1.name == "USD"). If the code does this a lot, consider making a map out of currencies:

currencies_map = Map.new(currencies, fn v -> {v.name, v} end)
  exchange_rates = %{
    "AUD" => %{"USD" => 1.14, "CVN" => 1.2},
    # OR, if an exchange rate is more complicated than just a number
    "USD" => %{"CVN" => %{rate: 0.67, quote_id: 1234}, "AUD" => %{rate: 1.06, quote_id: 4567}}
  }

The benefit of using nested maps here is that looking up an exchange rate is spelled exchange_rates[source_currency][destination_currency].

If there is more than one exchange rate for a given currency pair, this approach will not work. Consider using lists for that case.

exchanged_values =
  exchange_rates
  |> Map.new(fn {src_currency, dest_currencies} ->
    {
      src_currency,
      Map.new(dest_currencies, fn {dest_currency, exchange_rate} ->
        # do something with src_currency, dst_currency, and exchange_rate
        # probably look things up in currencies
        result = %{
          name: dst_currency, # or "#{src_currency} -> #{dst_currency}"
          exchange_rate: ... # calculate this
          value: ... # and this
        }

        {dest_currency, result}
      end
    }
  end

This results in a new map of maps; exchanged_values[source_currency][dest_currency] is a map just like the ones in currencies.

That last part about adding initial and final values to the same list seems tricky; how do consumers of R know how to interpret the {name: ..., exchange_rate:..., value:...} results?

One option would be to have R contain tuples:

exchanged_values
|> Enum.flat_map(fn {src_currency, dest_currencies} ->
  dest_currencies
  |> Enum.map(fn {dest_currency, final_value} ->
    # get initial_value for dest_currency from currencies
    {initial_value, final_value}
  end)
  |> Enum.filter(fn {initial_value, final_value} ->
    # decide if record should be included or not
  end)
end)

This uses Enum.flat_map for two reasons:

  • we’re building a list, but a single key of the input may return many values
  • some keys in the input may return NO values