Iex's ref/1

Invoking h() in iex tells me that ref/1 will create a ref from a string.

iex> ref("hi")

This errors, complaining about :erlang.list_to_ref('#Ref<hi>')

Cool, so I try:

iex> ref('hi')

It really doesn’t like that. It’s saying it can only match on a function with an is_binary guard, which just loops me back to my original thought.

What gives?

The pid() helper gives me similar woes.

To be clear, I’m not angry things are this way, I just don’t understand. Thank you in advance! :slight_smile:

ref/1 and pid/1 are convenience functions in iex to “re-create” refs and pids from a string representation. There is also ref/4 and pid/3 which works with integers.

A reference is a unique reference in an BEAM cluster. This is useful when you want a unique identifier for whatever reason (for example a transaction id). They are created with make_ref/0 (or :erlang.make_ref/0. A pid is a globally unique identifier to a process.

I agree that the help text for ref and pid could perhaps be improved. There purpose is to to be able to convert a pid/ref from a string representation into a ref/pid. Perhaps it should say “create a ref from a string representation of a ref”

iex(3)> r1 = make_ref()
#Reference<0.633323164.2506358787.64295>
iex(4)> ref(0, 633323164, 2506358787, 64295)
#Reference<0.633323164.2506358787.64295>
iex(5)> ref("0.633323164.2506358787.64295")
#Reference<0.633323164.2506358787.64295>
iex(6)> self()
#PID<0.105.0>
iex(7)> pid(0,105,0)
#PID<0.105.0>
iex(8)> pid("0.105.0")
#PID<0.105.0>
5 Likes

Ohhhhhhhh sh*t! Yes, that makes total sense. Creating a pid from a string representation is something I was wanting in iex experimentation weeks ago but figured it couldn’t be done. Thank you muchly for the explanation!

I agree the docs could be clearer. If they reflected your suggestion I would have not asked this question. I would propose that “Create a ref from its string representation” would be nice and concise.

1 Like

That still leaves the question of what is exactly a “ref”. After some experiment I figure it must be a very long integer that is usually expressed as a 4 integer tuple with some non-apparent rules:

iex(37)> ref("0.0.0.262144")
#Reference<0.0.0.262144>
iex(38)> ref("0.0.0.262145")
** (ArgumentError) argument error
    :erlang.list_to_ref('#Ref<0.0.0.262145>')
    (iex 1.10.3) lib/iex/helpers.ex:1268: IEx.Helpers.ref/1```

Haha, yes, indeed! I still don’t really understand refs, though this wasn’t my first encounter with them. When h()'ing in iex for an unrelated reason, I saw the helper and thought to try it and got confused (ie, see above).

While understanding refs wasn’t the intent of my question, I’m all ears if this thread wants to go that way. I have sen them in others’ code and all I can gather is that they are like UUIDs for Elixir, but I have yet to understand what problem they are solving. It’s on my list, but happy for anyone to chime in here!

Refs are a reference to “here and now”. They are designed to be probalistically unique across all erlang vms forever.

It’s a token to demarcate that something happened. A good example is a genserver call. When you call another server, you need a return address and a transaction id (you see similar things at the byte level in network protocols). So, use a ref for the txid, so that when the call is returned, the caller’s receive block can be sure that the reply is matched correctly. Similarly, elixir Task.async drops a ref in the response so that if you shoot off multiple asyncs, you can match the future to the correct await response.

Because these are probabilistically unique, you can safely use them in distributed erlang with no coordination overhead.

2 Likes