binarypaladin

binarypaladin

Using a compile time mutable registry?

Let me start by saying that while I have done some basic coding in Elixir and messed with Ecto and Phoenix I am coming from a mostly Ruby/class-based OOP background, and I can tell I have a lot brain-training to do. The main issue is dealing with immutable structures. It’s been smooth sailing thus far actually but… in a project I’m messing around with I’ve hit an issue.

Let’s say I have a struct/module named Thing. A Thing can be related to other things via some kind of value—let’s say I just have a key named relationships. The problem I see is when you have a struct of thing1 and thing2 that relate back and forth. If this was OOP and I was building a class or even a simpler structure like a hash, thing1 would get created first then you’d create thing2 and probably have it point back to thing1. From there you’d mutate thing1’s relationships to point to thing2. However, since I cannot mutate thing1, changing the relationships results in a new struct which points to thing2 which points to the old thing1.

At runtime it seems like you can accomplish this with a GenServer. All the Thing things understand that they need to talk to a GenServer and related a name. In this case, thing1 would have a reference to a name “thing2” that, at some point in the future, would resolve to the actual thing2 once defined.

What I really want is a global map available at runtime but generated dynamically at compile time. In terms of OOP, these structs are just “instances” of Thing. What would be better would be to have a module named ThingsRegistry with a function called get that took a string or atom and returned a named struct but, unlike a GenServer is not a process but rather the map that get is reading from has been set at compile time and perhaps register to replace the name map.

Can a macro take an existing function, get the return data, and then overwrite it?

I understand that I might be thinking about this all wrong and if that’s the issue, please feel free to let me know.

Most Liked

mindok

mindok

As always, it depends what you’re trying to do…

If you are looking at maintaining relationships between Things, probably the easiest way to look at it is to separate the entities from their relationships (i.e. model it as a digraph with nodes and edges - you could take a look at GitHub - bitwalker/libgraph: A graph data structure library for Elixir projects · GitHub for inspiration).

Other than that, looking up other entities by some kind of key is a pretty common approach. How that is implemented depends on what you are trying to achieve (in detail). For example, GitHub - lau/tzdata: tzdata for Elixir. Born from the Calendar library. · GitHub provides a timezone database for elixir - it builds the data and relationships in ets on startup (and periodically updates on a schedule when timezone databases update) and provides data back to consumers via lookups on the ets tables. Incidentally, it is building links between entities as part of the database build process to allow alias names for timezones.

al2o3cr

al2o3cr

What you’re describing sounds a lot like ETS except for the compile-time stuff.

The stdlib uses ETS to store graphs (see :digraph and :digraph_utils) because it’s a mutable key-value store that can be shared with other processes.

For a simpler example, here’s an Advent of Code solution from the 2018 problems that uses an ETS table as a doubly-linked circular list:

https://github.com/al2o3cr/advent-of-code/blob/d35005bc722d304005d538f572a9ab345f24e08f/day9/part2.exs

Each element in the list has an identifying “value” and the values of its left and right neighbors; the list is traversed by repeatedly looking up entries by the first value and then recursively following the links.

For that problem, the structure was really convenient: each update was a fixed, small number of steps way from the previous update and inserted or removed a single element. That meant changing three entries in ETS, versus rewriting every following entry in a map or list.

Qqwy

Qqwy

TypeCheck Core Team

(emphasis mine)

I think we might have a case of the xy-problem here: You are looking for help with your supposed solution, but do not give us the full problem which makes it difficult to reason about the solution space. We’re slowly getting closer, as we now know that what you actually want to do has very little to do with ‘compile-time registries’.

You think you want to use bidirectional relationships between e.g. parents and children. However, what would you use these relationships for?
In object- and class-oriented languages where we deal a lot with inheritance, (bi-directional) relationships see a lot of use, especially since pass by (mutable) reference is very common, which is what gave rise to Joe Armstrong’s famous quote You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.

In an immutable functional language such as Elixir, we’d model the problem usually in a very different way where these mutable references are not needed. In these situations we often pick only one direction to go in (such as only parent → child) and that is how we fill in our structs. This is what e.g. Ecto’s relationships use.

But say we are indeed trying to model the ancestry of a family of humans, which is one of the few cases where indeed relationships in both directions might be important. For instance, we want to figure out who all the nieces and nephews of a particular family member are.
In that case, we use a dedicated graph datastructure. (which can be implemented under the hood in a number of ways on an immutable system, which I won’t go in here as it’s not important when using them). One example might be using libgraph but there are other libraries as well.

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