Printing references as strings?

I’m just discovering make_ref/0 and I’m wondering how to print them as a string… I have poked through the source code, but I don’t see any way to see inside the reference values.

2 Likes

This worked for me:

iex(5)> "#{inspect make_ref}"        
"#Reference<0.4290490855.1120927745.192473>"

Never used references before but it seems it can be printed just fine. Or are you looking for a certain property?

Don’t expect to. A reference is just a marker to a “place and time”. They’re usually otherwise empty values that are “guaranteed” to be unique. Now, sometimes they are coopted by the vm to refer to “something” but by and large they aren’t references like references in the JVM.

For example, let me send a call to another process. When the response comes back, it should somehow refer to the initial call, so that this response isn’t structurally confused for a callback for some other call (that maybe got dropped and thus orphaned). The thing that the call sends, which should be packaged with the response, so that it can be can referred back to correctly is a reference.

3 Likes

demo, you can see the return address PID and a reference packaged with the call content quite clearly:

iex(1)> self()
#PID<0.105.0>
iex(2)> pid = spawn(fn -> receive do any -> any end |> IO.inspect end) # trap the next message and print it
#PID<0.108.0>
iex(3)> GenServer.call(pid, :foo) # pretend the spawned thread is a genserver so we can peek inside the message

{:"$gen_call", {#PID<0.105.0>, #Reference<0.27482903.3541827588.143960>}, :foo}

(followed by the call failing because the called pid has stopped itself)

1 Like
make_ref()
|> :erlang.ref_to_list()
|> List.to_string()
3 Likes
** (UndefinedFunctionError) function :erlang.reference_to_string/1 is undefined or private. Did you mean one of:

      * ref_to_list/1

    :erlang.reference_to_string(#Reference<0.1918863630.4161011720.226556>)

Yeah, sorry, it should be :erlang.ref_to_list/1.

3 Likes

I should clarify the use here: I’m working on a (mostly academic) exercise that seeks to run code in multiple modules, sometimes with different runtime values given to the same module, so I want a unique identifier for each “instance” (if you’ll forgive the OO speak). A reference seemed the most appropriate way to do this. The script may generate a lot of output which will be useful for reporting and data analysis, so a CSV or similar representation makes a lot of practical sense. So the question is simply how to represent the unique reference as a string inside of a CSV or similar file. inspect/1 is fine inside iex, but for the purposes of analysis, a simple textual representation is the only thing that wouldn’t look “weird”.

Plan B would be to use a UUID.

Remember that references are meant to be unique only within single cluster, there is no guarantee for global uniqueness of the reference, in contrast to UUID.

3 Likes

Uuids also have a slightly weak guarantee. Collisions are possible. However, you’ll probably be ok.

Much stronger than make_ref/0 (64 bits vs 122 bits, which with birthday collision will provide 1010 difference factor in possible collisions). And generating UUID can be done also outside of the BEAM, which is clearly a win for me.

4 Likes

True, however the fact that most libraries compare them as strings rubs me off the wrong way. I’d feel much better if UUIDs were actually treated as byte arrays and compared as such (or in Erlang/Elixir’s case, fixed-size tuples of integers). IMO that leaves some untapped performance potential on the table – even if I know it’s likely negligible it still worries me.

1 Like

Even worse is django stores them in the DB as a base16 string :grimacing:. If you’re gonna do that, at least keep the hyphens.

1 Like

Ouch! You do gotta wonder what horrendous legacy compatibility reasons made them do that…

Elixir and Erlang libraries mostly use binaries for storing UUID which IMHO makes most sense.

2 Likes

Actually yep, you are right.

Hi there, I didn’t catch the takeaway here. I think I have a similar use case. I just want a unique-ish ID to identify a particular connection to a 3rd party API. this post on SO claims that make_ref/0 is the canonical way but I’m not sure how to convert that to something like a string of numbers to use as a param for the call to the API?
I’ve used UUID and Ecto in the past but trying to understand the “usual approach” and minimized external dependencies for something that shouldn’t need those libs at this time. Any advice?

:rand.bytes(16)   # If you are at OTP 24.0 or above
:crypto.strong_rand_bytes(16)   # More backwards-compatible but basically the same

…which gives you 16 random bytes, which you can then format as hex strings, or anything else you need.

These two are pretty much different wrt. randomness guarantees

True, I was kind of referring to the API contract. :smiley: