Skip to content

Guard clause in for comprehension apparently applied AFTER dependent clause is evaluated, leading to confusing results #5509

@myronmarston

Description

@myronmarston

Environment

  • Elixir version (elixir -v):
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Elixir 1.5.0-dev (6b0aae6)
  • Operating system:

OS X El Capitan

Current behavior

Guard clauses in for comprehensions are supported, and cause the comprehension to ignore unmatched values:

iex(1)> binding = [{:my_var_2, 2}, {{:my_var_1, Elixir}, 1}]
[{:my_var_2, 2}, {{:my_var_1, Elixir}, 1}]
iex(2)> for {var_name, _} when is_atom(var_name) <- binding, do: var_name
[:my_var_2]

However if you add a dependent clause that assumes that it will only apply to matched values, you can see that it fails:

iex(3)> for {var_name, _} when is_atom(var_name) <- binding, var_name = Atom.to_string(var_name), do: var_name
** (ArgumentError) argument error
             :erlang.atom_to_binary({:my_var_1, Elixir}, :utf8)
    (stdlib) erl_eval.erl:670: :erl_eval.do_apply/6
    (stdlib) erl_eval.erl:438: :erl_eval.expr/5
    (stdlib) erl_eval.erl:269: :erl_eval.expr/5
    (elixir) lib/enum.ex:1755: Enum."-reduce/3-lists^foldl/2-0-"/3

Expected behavior

I would expect that Atom.to_string(not_an_atom) would never be called in this case because we matched on only atoms.

See #5504 for an example of where this came up in practice.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions