Elixir `when` oddness (Elixir bug?)

Is this documented anywhere, but apparently you can have multiple when clauses on a function (and they seem to or), an example:

iex> defmodule Testeringa do
...> def blah(x) when is_integer(x) when is_float(x), do: x
...> end

{:module, Testeringa,
 <<70, 79, 82, 49, 0, 0, 4, 200, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 145,
   131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
   95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:blah, 1}}
iex> Testeringa.blah(42)
42
iex> Testeringa.blah(6.28)
6.28
iex> Testeringa.blah(:ok)
** (FunctionClauseError) no function clause matching in Testeringa.blah/1
    iex:13: Testeringa.blah(:ok)

So… is this a bug or is it expected? o.O
If it is expected then where is this documented, I’m not able to locate it…

4 Likes

It is working as expected. Documented here: https://hexdocs.pm/elixir/guards.html#multiple-guards-in-the-same-clause.

3 Likes

The documentation seems to be wrong, though - the two forms are not equivalent semantically:

iex> match?(x when map_size(x) < 1 or tuple_size(x) < 1, {})
false
iex> match?(x when map_size(x) < 1 when tuple_size(x) < 1, {})
true

The guard fails if it evaluates to false or crashes. The or is a normal expression so is not evaluated further after a crash, while multiple whens are evaluated sequentially in case one does not match (or crashes).

I know this property is not exploited commonly in elixir, but it’s there and can cause troubles. Those semantics could be changed if Elixir compiled to Erlang Core directly, since the error meaning guard does not match is established there explicitly - Elixir could opt-out from it.

7 Likes

Fascinating, so it is like the sequence guard clause in Erlang (but just multiple when's :slight_smile: ), cool so I understand it now. Thanks! :slight_smile:

Since it exists now I need to handle it for typing, so I’ll pretend like each when is a unique function head when, simple enough. :slight_smile:

3 Likes

Yes, you are correct. Please send a PR. :heart:

To clarify, the semantics are as you describe on purpose.

2 Likes

Done https://github.com/elixir-lang/elixir/pull/5782

2 Likes

It can still be done if Elixir were to compile directly to Core, you just have to get it right. You just copy how Erlang does it.

You know I’m curious, is there good documentation on the entire Core format somewhere?

No, not that I have been able to find anyway. :grinning:

Heh, I’d looked myself but figured I was just missing the obvious or something. ^.^