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.