Use guards on anonymous functions

Background

I have a filter to where I am passing an anonymous function. In this function I want to check that the key of the tuple I am evaluation is a string and if so return true, like in the following code:

:persistent_term.put("hello", :world)

:persistent_term.get() 
|> Enum.filter(fn {key, _val} when is_binary(key)-> true end)

Error

However, when I do so, I get an error:

** (FunctionClauseError) no function clause matching in :erl_eval."-inside-an-interpreted-fun-"/1    
    
    The following arguments were given to :erl_eval."-inside-an-interpreted-fun-"/1:
    
        # 1
        {Logger.Config.DeletedHandlers,
         [
           {:primary, %{level: :notice}},
           {:default, :logger_std_h,
            %{
              config: %{
                burst_limit_enable: true,
                burst_limit_max_count: 500,
                burst_limit_window_time: 1000,
                drop_mode_qlen: 200,
                filesync_repeat_interval: :no_repeat,
                flush_qlen: 1000,
                overload_kill_enable: false,
                overload_kill_mem_size: 3000000,
                overload_kill_qlen: 20000,
                overload_kill_restart_after: 5000,
                sync_mode_qlen: 10,
                type: :standard_io
              },
              filter_default: :stop,
              filters: [
                remote_gl: {&:logger_filters.remote_gl/2, :stop},
                domain: {&:logger_filters.domain/2, {:log, :super, [:otp, :sasl]}},
                no_domain: {&:logger_filters.domain/2, {:log, :undefined, []}}
              ],
              formatter: {:logger_formatter, %{legacy_header: true, single_line: false}},
              id: :default,
              level: :all,
              module: :logger_std_h
            }}
         ]}
    
    (stdlib) :erl_eval."-inside-an-interpreted-fun-"/1
    (stdlib) erl_eval.erl:829: :erl_eval.eval_fun/6
    (elixir) lib/enum.ex:2942: Enum.filter_list/2

What really makes this confusing is that a similar function works as expected:


:persistent_term.get()
    |> Enum.filter(fn {key, _val} -> if is_binary(key) do true else false end end)

They both do the same, except the second one works and the first one doesn’t. According to the documentation I know I can use guards in anonymous functions:

https://hexdocs.pm/elixir/master/guards.html#where-guards-can-be-used

So, it is clear to me that I am doing something wrong.

Questions

1 .What am I missing?
2. Is this the most compact way of verifying if a key is a string?

1 Like

Enum.filter must be able to apply the given function to each individual input. In your example, that’s not the case (some inputs in the collection don’t satisfy the guard).

To fix it, add a clause matching _ that simply returns false:

:persistent_term.get() 
|> Enum.filter(fn
    {key, _val} when is_binary(key)-> true
    _ -> false
end)

Although I would personally find this more readable for what you’re trying to do:

Enum.filter(:persistent_term.get(), fn {key, _val} -> is_binary(key) end)
5 Likes

:wave:

Would that work?

:persistent_term.get() 
|> Enum.filter(fn {key, _val} -> is_binary(key) end)
4 Likes