Use guards on anonymous functions


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)

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


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
           {: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:

    |> 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:

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


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:

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

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)


Would that work?

|> Enum.filter(fn {key, _val} -> is_binary(key) end)