Fl4m3Ph03n1x

Fl4m3Ph03n1x

Guard test breaks the opacity of its argument

I have the following code where I test for the existence of an ETS table:

case :ets.whereis(:metrics) do
      :undefined                  -> {:error, :table_not_created}
      tab when is_reference(tab)  -> {:ok, :ready}
      _                           -> {:error, :unable_to_veify_table}
end

However, the clause is giving a weird error:

Guard test is_reference(_tab@1::ets:tid()) breaks the opacity of its argument

What does this mean and how can I fix it?

Marked As Solved

LostKobrakai

LostKobrakai

The point of opaque types is that the module, which defines the type has functions, which know how to handle the data. In the case of ets the :ets module knows what the data behind tid() is and what to do with it. new does return it, while lots of other functions use it. It does not matter if the current implementation of tid is an integer or a reference, as any code which is not inside of :ets is just supposed to hold on to the value, but not look at what’s inside. This allows :ets to refactor what tid() actually is without breaking client code.

Another example of an opaque type is MapSet.t. Behind the scenes mapsets are implemented using a map. But a map alone cannot guarantee the uniqueness required for sets. Therefore the datatype needs to be opaque so that (in a perfect world) nobody ever directly manipulates the data, but people need to use functions on the MapSet module for manipulation. The module can then guarantee that the constraints of a set datatype are adhered to.

Like mentioned for :ets the implementation for MapSet did already change over the livetime of elixir as well.

There are also tradeoffs to be made when using opaque types:

Only the single module defining the opaque type can actually do something with the data it holds. So the module needs to have functions for everything a client might want to do with said data. No other module can enhance or alter the behaviour around it without breaking the type contract.

Another caveat for opaque types is that you need to be careful with longterm storage of such types. If the runtime updates, but the stored value doesn’t it can result in failures.

Also Liked

garazdawi

garazdawi

Erlang Core Team

It actually used to be an integer not very long ago, so :ets is a very good example of this.

LostKobrakai

LostKobrakai

You can’t really fix it. :ets.whereis returns the opaque type tid() but your guard clause does typecheck on it. This means you’re dealing with internals you’re not supposed to deal with.

sasajuric

sasajuric

Author of Elixir In Action

You can rely on the fact that all the :ets functions work correctly if you pass them the result of :ets.new, regardless of what the actual return value is. As long as you do that, and don’t assume anything about the actual data in the tid type, you shouldn’t experience any breaking changes if the tid type is changed.

peerreynders

peerreynders

In my mind an opaque type is the equivalent to the OO design guideline that all instance data should have non-public access.

In OO one motivating factor is that you don’t want anything but the instance methods mutating the instance data. In a world where everything is immutable by default that really isn’t an issue which explains to some degree why functional languages can rely on plain data structures so much.

However direct access of the client to the data to the internal organization of the data structure still couples the client to that specific organization.

An opaque type breaks that type of coupling as the client is now forced to use the functions that are supplied with the data structure to extract any information.

When done right an opaque type can give the freedom to radically change the implementation while keeping the API of client facing functions stable (example - here names could be typed as @opaque).

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

I don’t think this is a static vs dynamic typing thing. Statically typed languages wrap built in types in new types all the time which effectively creates opaque types.

@Fl4m3Ph03n1x The return value of the api is tid(). You can use it for any function that takes a tid(). Opaqueness just means that you shouldn’t look deeper into it to try to figure out what a tid() is composed of because tid() doesn’t make any promises about that.

It’s a little like a MapSet. MapSet.new([1,2,3]) returns a MapSet.t and you can pass it to functions that accept that type. You shouldn’t try to do break apart the MapSet and grab the internal map out of it to try to mess with. That might change.

Where Next?

Popular in Questions Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
joaquinalcerro
Hi there, I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 record...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
makeitrein
Hey all, just started picking up Elixir last week and am writing a scraper as a learning project. Baby step #1 is extracting the number ...
New
minhajuddin
I have seen a lot of code which picks the first element from a list using Enum.at(0) instead of List.first. Is there a reason why people ...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New

Other popular topics Top

shahryarjb
Hello, I get Persian date from my client and convert it to normal calendar like this: def jalali_string_to_miladi_english_number(persi...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
sergio_101
I am VERY much an elixir newbie. I have taken one elixir course and one phoenix course on Udemy. During that course, I saw the instructor...
New
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 49522 488
New
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
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
aesmail
Hello guys, I have finally made it. I created an admin interface for a framework. It’s been on my todo list for years and with the curre...
New
siddhant3030
Hi, I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New

We're in Beta

About us Mission Statement