sodapopcan
Why does `elem/2` exist?
I’ve been in the Elixir world for five years—not super long but also not super short. In this time I have seen lots of code that uses elem/2 but not once have I ever seen a situation where it made code clearer and in fact has always made code less clear. It seems its main use it to “not break pipelines.” To put it lightly: this is a non-goal. I have never run into a situation where pattern matching isn’t way way way waaaaaaaay clearer than elem/2, even if it means breaking the pipe.
IMO if we ever see an Elixir 2.0 this function should be removed from the language. But please tell me why elem/2 is indispensable to your Elixir work! I realize I’m using very dismissive language but I’m genuinely curious if there is a good use-case for it I’ve never thought of.
Marked As Solved
zachallaun
An example from a project I contribute to: Lexical.Document.Lines
The key insight is that a tuple provides O(1) access to any of its elements, whereas a list is O(n). So if you need a data structure that provides fast access to any arbitrary line of a document, you reach for a tuple. (And then you use elem/2 for access, since you don’t know what its length will be.)
Also Liked
dorgan
- You have tuples, a fixed size array of elements.
- You can access them by a random index.
- There is no syntax to access a random index of a tuple
- Pattern matching requires you to know the length of the tuple up-front
I think it makes sense to have such a function be part of the standard library. The reasoning is quite simple: if you provide a builtin data structure, it makes sense to provide an API to read and write to it.
Wether or not it’s idiomatic to access a random element of a tuple is a whole different topic.
dimitarvp
sodapopcan
Everyone is always free to go off topic in any thread I create ![]()
LostKobrakai
It’s main use is accessing a tuple value using an index, which works no matter the size of tuple and in context, where pattern matching is not an option. Just like there’s :erlang.map_get for maps, because pattern matching is not enough.
There’s places in OTP, where you might get differently sized tuples, where leading parts of them contain the same data. You can use elem/2 to select the common fields without needing to know which specific tuple you got if the differenciation doesn’t matter for the code. It also means your code doesn’t break if OTP decides to add another version of tuple with yet another additional datapoint added to the end.
If flag
timestamp,strict_monotonic_timestamp, ormonotonic_timestampis specified, the first tuple element istrace_tsinstead, and the time stamp is added as an extra element last in the message tuple.
Usually one would likely use maps for such a usecase, but sticking to tuples for performance sensitive calls like around tracing is also a good idea.
Also e.g. elixir’s record handling code wouldn’t be a lot more complex macro magic without having such functionality. Technically it doesn’t use Kernel.elem/2, but it optimizes by calling :erlang.element/2 directly, which is also what Kernel.elem/2 is using.
al2o3cr
Short short version: elem/2 exists because typing :erlang.element is too many characters.
Record also provides some examples of places where elem (or it’s Erlang equivalent) are useful, for instance is_record?:
My interpretation of the release notes about setelement optimizations is that sequences like this one (also from Record) may get a boost from the compiler and/or JIT:
Since the intermediate tuples don’t escape the function, the reduce could do all the mutation in-place.









