Blog Post: 10 Elixir gotchas

The difference is that you can do a lot of things with maps. For example, I can write:

def foo(%{} = map) do
  Enum.reduce(map, ..., ...)
end

And that code won’t work for structs or it will work potentially enumerating something else (not tuples representing pairs). Or trying using the access behaviour. You can’t really do many interesting things with atoms besides comparing them, so in my experience, those mix-ups rarely happen. Even in the example you provide, most code checks if something exists or not by precisely checking is_nil/not is_nil (YMMV).

EDIT: That said, we could 100% add a disclaimer to the is_atom/1 guard if there isn’t one yet. Plus this is another scenario that can be caught by the type system because in such cases it would know is_atom/1 would always return true.

It would 100% be a breaking change. For example, implementation of true/false/nil is done via the Atom protocol. So we can’t keep them separate without breaking that. Dialyzer and Erlang as a whole would not share our interpretation of atoms, etc.

3 Likes

+1 for the typing check as it’s an easy path forward without breaking anything. The proposal for another guard was specifically to instruct the type system, seems it is not needed.

That’s where the “in the sense” came from. Technically it would be a breaking change, in practice it would probably just align with what people already expected from it (before/without learning the gotcha)

I consider the gotcha gone the day the type systems warns. Until then the is_nil gotcha earns it’s place for the “ow snap, better remember”.

Got a new gotcha today

Elixir’s ++/2 operator (list concatenation) has a special clause: If the right-hand side is not a list, the operation returns that right-hand side value instead of raising an error.

Never knew about it. Did bite me. Type checker did not warn that the enumeration would possibly
raise an exception. :frowning:

iex(1)> [] ++ nil
nil
iex(2)> [] ++ :foo
:foo
iex(3)> [] ++ "bla"
"bla"

The source in erlang is clear. This is the comment on the nil as right-hand side:

if (is_nil(lhs)) {
        /* This is buggy but expected, `[] ++ 'not_a_list'` has always resulted
         * in 'not_a_list'. */
        return rhs;

Doubt I like the behavior, but alas…there it is.

7 Likes

wow never knew! Thanks for that!

I still wanna do a second edition of this but never quite got around to it :sweat_smile:

To quote an old movie…

“Right. That’s bad. Okay. All right. Important safety tip. Thanks, Egon.”

1 Like