kip

kip

ex_cldr Core Team

[snippet] Debugging Function Clause errors

A little while ago someone asked about how to get a full list of function clauses when there is a function clause error. I have some code that generates a lot of function clauses so I had a similar need. I couldn’t find my code at the time but I have now and post it here so I can find it again :slight_smile:

Example

iex> FunctionClause.match MyApp.Cldr.Number.Formatter.Decimal, :to_string, ["123", "#", []]
    def to_string(number, format, options) when is_binary(format) and is_list(options)
    def to_string(number, "#,##0%", options) when is_map(options)
    def to_string(number, "#,##0.###", options) when is_map(options)
    def to_string(number, "#,##0.00 ¤", options) when is_map(options)
    def to_string(number, "#,##0.00 ¤;(#,##0.00 ¤)", options) when is_map(options)
    def to_string(number, "#,##0 %", options) when is_map(options)
    def to_string(number, "#E0", options) when is_map(options)
    def to_string(number, "0", options) when is_map(options)
    def to_string(number, "0 Billion", options) when is_map(options)
    def to_string(number, "0 Billionen", options) when is_map(options)
    def to_string(number, "0 Milliarde", options) when is_map(options)
    def to_string(number, "0 Milliarden", options) when is_map(options)
    .... and a lot more :-)

Code

defmodule FunctionClause do
  @moduledoc """
  Format function clauses using Exception.blame/3
  """

  @doc """
  Given a `module`, `function`, and `args` see
  that function clause would match or not match.

  This is useful for helping diagnose function
  clause errors when many clauses are generated
  at compile time.

  """
  @spec match(module(), atom(), list(any)) :: :ok | no_return()
  def match(module, function, args) do
    case Exception.blame_mfa(module, function, args) do
      {:ok, kind, clauses} ->
        formatted_clauses(function, kind, clauses, &blame_match/2)

      :error ->
        raise ArgumentError,
              "Function #{inspect(module)}.#{function}/#{length(args)} " <>
                "is not known."
    end
  end

  defp formatted_clauses(function, kind, clauses, ast_fun) do
    format_clause_fun = fn {args, guards} ->
      code = Enum.reduce(guards, {function, [], args}, &{:when, [], [&2, &1]})
      "    #{kind} " <> Macro.to_string(code, ast_fun) <> "\n"
    end

    clauses
    |> Enum.map(format_clause_fun)
    |> Enum.join()
    |> IO.puts()
  end

  defp blame_match(%{match?: true, node: node}, _),
    do: Macro.to_string(node)

  defp blame_match(%{match?: false, node: node}, _),
    do: IO.ANSI.red() <> Macro.to_string(node) <> IO.ANSI.reset()

  defp blame_match(_, string), do: string
end

Where Next?

Popular in Guides/Tuts Top

sergio
https://sergiotapia.me/generate-images-with-name-initials-using-elixir-and-imagemagick-374eca4d14ff Hope this saves you guys some time!...
New
Eiji
Hey, today I give amnesia library a try and found a few problems. I would like describe how to setup it properly and solve problems which...
New
voltone
The EEF’s Security WG has released the first public draft of the Secure Coding and Deployment Hardening Guidelines for BEAM languages. “...
New
nelsonic
When we were figuring out how to use Phoenix LiveView we got stuck a few times. So in order to save other people time, we created a comp...
New
annad
I’m posting this for developers who are totally new to Phoenix like myself. This is all probably obvious to more skilled Phoenix develope...
New
TwistingTwists
This is a thread to note down things/best practices encountered in LiveBeats App as I explore the source code. https://github.com/fly-...
New
slouchpie
Warmest greetings, comrades. I recently started using :dns_cluster (GitHub - phoenixframework/dns_cluster: Simple DNS clustering for dis...
New

Other popular topics Top

minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
msaraiva
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
564 44167 214
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New

We're in Beta

About us Mission Statement