Enum.any - same list in two different orders gives me two different answers

On the hex docs it tells us that enum.any? will return truthy if any element in the list is truthy, or the function applied to any element on the list returns truthy… However, when I apply the following function with the same list in two different orders it gives me two different answers.

Enum.any?(["hello", 1..3, false], fn x -> x <> "yo" end)

true

Enum.any?([1..3, "hello", false], fn x -> x <> "yo" end)

** (ArgumentError) argument error while evaluating iex at line 11
(stdlib 3.15.1) eval_bits.erl:123: :eval_bits.eval_exp_field1/6
(stdlib 3.15.1) eval_bits.erl:81: :eval_bits.create_binary/2
(stdlib 3.15.1) eval_bits.erl:72: :eval_bits.expr_grp/4
(stdlib 3.15.1) erl_eval.erl:481: :erl_eval.expr/5
(elixir 1.12.2) lib/enum.ex:3792: Enum.any_list/2

hi Kimberly,
Could you please use triple backticks to mark your code, “```”
also make sure it is proper Elixir code, as your editor is replacing …, and doble quotes.

Is how I edited it just now correct?

1 Like

you are trying to concatenate a range to a string. ie 1..3 <> "yo" and that does not make sense.

1 Like

Yes, it is. I copied and pasted your code and answered you straight away because i could clearly see what was going on.

1 Like

You are probably after this, to determine if any element in the list is a string.

Enum.any?(["hello", 1..3, false], &is_binary/1)

1 Like

Enum.any?/2 will only call the function as many times as it has to - if it sees a truthy result it stops evaluating immediately.

Take a look at the source; for a list input, this any_list/2 implementation is used:

Line 3974 is the early exit.

In your example, the first case evaluates fn x -> x <> "yo" end with x bound to "hello", gets "helloyo" as a result, and Enum.any? returns true.

In the second, Enum.any? tries to evaluate the function with x bound to 1..3, which raises an ArgumentError.

4 Likes

Okay I see. Thank you. So if I had an element in the list Enum. any? that was false, it would have kept going through the list until it found one that was true, or until it gets stuck on an argument error.

2 Likes

Yes, correct.

2 Likes

Just to clarify:

So if I had an element in the list Enum. any? that was false, when applied to the passed function it would have kept going through the list until it found one that was true when applied to the passed function, or until it gets stuck on an argument error.

2 Likes

Thanks :slight_smile:

1 Like

I should have demonstrated and not give a two-word answer:

[1, 2, 3, 4, 5, :a, :b]
|> Enum.any?(&is_atom/1)

This will iterate up until :a (included) and return true. It will not get to :b because the meaning of Enum.any? is to find just one element for which the provided function returns true. So when it finds it, it stops processing.

1 Like