sodapopcan

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

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

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.

15
Post #3
dimitarvp

dimitarvp

Agreed, but it has to be noted that then – which solves the problem of not breaking pipelines – was only added in Elixir 1.12.

Use then.

sodapopcan

sodapopcan

Everyone is always free to go off topic in any thread I create :sweat_smile:

LostKobrakai

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, or monotonic_timestamp is specified, the first tuple element is trace_ts instead, 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

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?:

https://github.com/elixir-lang/elixir/blob/47abe2d107e654ccede845356773bcf6e11ef7cb/lib/elixir/lib/record.ex#L136-L138


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:

https://github.com/elixir-lang/elixir/blob/47abe2d107e654ccede845356773bcf6e11ef7cb/lib/elixir/lib/record.ex#L472-L478

Since the intermediate tuples don’t escape the function, the reduce could do all the mutation in-place.

Where Next?

Popular in Questions Top

Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
Patoshizzle
After calling mix ecto.create I get this error: 17:00:32.162 [error] GenServer #PID<0.412.0> terminating ** (Postgrex.Error) FATAL...
New
vac
Hi, I’m quite new in Elixir and I’m trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and I...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
hariharasudhan94
lets say i have a sample like a = 20; b = 10; if (a > b) do {:ok, "a"} end if (a < b) do {:ok, b} end if (a == b) do {:ok, "equa...
New
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
srinivasu
How to handle excepions in elixir? Suppose i have A, B, C ,D, E modules. and each module has get() function. A.get() method will call t...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New

Other popular topics Top

skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
dokuzbir
I want to highlight html closing tags when i click a html tag. That works in .html files but doesnt work for html.eex templates. How can...
New
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
freewebwithme
Using vs code and installed ElixirLS: support and debugger. And I got an error popped up on start up says Failed to run ‘elixir’ comma...
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New

We're in Beta

About us Mission Statement