What’s going on here?

As I said before, this might be surprising, but it’s actually pretty obvious and well documented. Matching variables in case clause headers is perfectly valid and it can be used for stuff like this:

B = case rand:uniform() of

C when C < 0.5 -> low;

C when C >= 0.5 -> high

end,

{B, C}.

It’s a very convoluted way of doing things, I don’t recommend it at all, and you can even make it a bit worse…

case rand:uniform() of

C when C < 0.5 -> B = low;

C when C >= 0.5 -> B = high

end,

{B, C}.

…but, style and maintainability concerns aside, this code is totally valid and what that means is that variables that are bound in case clauses (both in the head and the body) remain bound after the evaluation of the case expression. I know there is a more academic way to express this situation using words like closure or scope but I’ll leave that explanation to folks like iraunkortasuna who are much more capable than me.

With that in mind, it’s reasonable that in our original example either C or D (but not both) are bound after the evaluation of the whole expression. But then, you wonder, that code is extremely unsafe… shouldn’t Erlang warn me against it? Shouldn’t Erlang prevent me from writing such a non-deterministic thing?

And of course, it does! But it’s the Erlang compiler, not the shell, the one that warns you about things like this. And it is really smart. Check the following module, for instance:

This is our original code and, in this case, even when either C or D are unbound after the case statement, since they’re not used, the compiler says nothing.

But now check this other module:

In this case, the compiler will not even compile the module. Instead, it will fail with the following error:

x.erl:8: variable 'C' unsafe in 'case' (line 6)

We can go off a tangent talking about how usable or readable that error message is, but the fact that the module can’t be compiled is undeniably useful.