l3nz

l3nz

How do you check that a MapSet is a MapSet?

I have a function where I do:

def recomputeCapacity(%MapSet{} = mCallIds) do
    cu =
      mCallIds
      |> MapSet.to_list()

And Dyalizer says that the function can never complete (and all the functions calling it…) because:

The call 'Elixir.MapSet':to_list
         (_mCallIds@1 :: #{'__struct__' := 'Elixir.MapSet', _ => _}) does not have an opaque term of type 
          'Elixir.MapSet':t(_) as 1st argumentElixirLS Dialyzer

Now, if mCallIds was an integer or something I’d use a guard is_integer(), but how do I make sure that only a MapSet can match this function?

I could have a

@spec recomputeCapacity( MapSet.t(any) ) :: number

But it would not prevent me calling the function by mistake, and if I leave the %MapSet{} = mCallIds Dialyzer keeps complaining.

I know that MapSet is opaque so I should not pattern-match on it (though - annoyingly - it works…) but what is the alternative?

Most Liked

LostKobrakai

LostKobrakai

Maybe. The problem with MapSet imo is less how opaque types work though, it’s that MapSet is not actually meant to be a full opaque type.

MapSets being a struct is a stable contract. Only all the other keys on the map are considered implementation details. That’s a usecase dialyzer is not able to handle – partial opaqueness in maps per key. If MapSets would be a opaque datastructure in the sense dialyzer treats it then you’d never see any documentation matching on it being a struct in the first place or any other reference to it’s underlying datatype.

E.g. I’d never check if a tid is a valid ets tid. I don’t even know what to check it by. I just use it as one and if it blows up it might have not been.

Qqwy

Qqwy

TypeCheck Core Team

:thinking: It seems like this is a known limitation of Dialyzer

What I said about pattern-matching on a struct that is defined as an opaque type is correct in theory, but in practice it seems that Dialyzer does not follow this logic correctly in certain situations.

To be precise: An opaque type hides even the fact whether something is a struct or not. (The Erlang/Dialyzer type system, which Elixir inherited, does not treat structs differently from maps.) Thus the pattern match is not strictly allowed. This makes the MapSet.t type impossible to use in combination with any pattern-match.

Personally I’d keep the runtime check (because it is enforced rather than opt-in), and change the @spec to use %MapSet{optional(any) => any()} instead.

Qqwy

Qqwy

TypeCheck Core Team

This is a good question. The pattern match you are doing, on %MapSet{}, is indeed the correct way to ensure that only MapSet-structs can be passed to the function. It is OK to patternmatch on it. It is only wrong to match on any particular fields the struct may or may not have (as these may change in future versions without warning).
This is the same for all opaque structs (and in this case mentioned explicitly at the end of the module’s documentation as well.)

Furthermore, your spec, using MapSet.t(any), is correct. It is an opaque type (so you’re not supposed to depend on its details), but internally it also is the same as a %MapSet{}-struct.

I’m not immediately seeing where the problem of Dialyzer comes from. What you could try, is to just use the type MapSet.t() instead of MapSet.t(any). It means the same, but maybe it confuses Dialyzer less in this situation?

LostKobrakai

LostKobrakai

Very much this. Opaque types are meant to be treated as completely opaque. It could be a struct today and a tuple tomorrow. Therefore even checking for the struct type is a violation of the opaqueness. It’s unlikely for such a change to happen for MapSet, but that’s besides what dialyzer tries to assert.

Just for an example: ets at some point changed the underlying datatype for :ets.tid. So changes like switching even the underlying datatype do indeed happen sometimes.

l3nz

l3nz

I agree - but shouldn’t an opaque type offer an is_me?() method, so that you can know that it is itself?

Where Next?

Popular in Questions Top

yawaramin
In the Dialyzer docs ( http://erlang.org/doc/man/dialyzer.html#requesting-or-suppressing-warnings-in-source-files ), there is a way to tu...
New
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
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
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
Exadra37
Sometimes I want to check if the input into a function is not a blank string. My first approach: defmodule Example do def do_stuff(s...
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
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
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
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
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
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
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
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
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