Issue with initializing nested MapSets

I ran into the following situation when trying to create a MapSet of MapSets yesterday. If you nest the MapSet.new() calls they get flattened. I don’t know if this is a bug or I am just unaware of the reason this should be the expected behavior. Does anyone have an explanation?

iex(1)> MapSet.new(MapSet.new([1,2]))
#MapSet<[1, 2]>
iex(2)> a = v(1)
#MapSet<[1, 2]>
iex(3)> MapSet.size(a)
2 # ← expected 1
iex(4)> MapSet.put(MapSet.new(), MapSet.new([1,2]))
#MapSet<[#MapSet<[1, 2]>]>
iex(5)> MapSet.size(v(4))
1 # ← this is what I wanted

1 Like

Then what you need is MapSet.new([MapSet.new([1,2])])

That works but it’s weird. I guess if the passed argument is Enumerable the MapSet.new() call iterates over each item in the passed argument to build the set. So in my original command it’s making a MapSet<[1,2]>, turning it into a list, iterating over that list, and putting each item into a MapSet<[1,2]>. I guess that makes sense. I guess I think it’s a little odd that MapSet.new requires that the passed arg be enumerable. Why is that? Would be nice to say MapSet.new(1) → MapSet<[1]>.

@spec new(Enum.t()) :: t
  def new(enumerable)

  def new(%__MODULE__{} = map_set), do: map_set

  def new(enumerable) do
    map =
      enumerable
      |> Enum.to_list()
      |> new_from_list([])

    %MapSet{map: map}
  end

The enumerable is converted to a list and then each element added to the map set. You can see its implementation here: elixir/map_set.ex at 14dff5c7613d241c29312d73ec1ae001a3c5f7af · elixir-lang/elixir · GitHub

In the case of passing a mapset, there’s an optimization, so no conversion is done.

If you want to create a map of one element, you wrap that element in a list like I did, but if Elixir would work as you suggest, if you want to create a map set of 5 elements, how would Elixir know that MapSet.new([1, 2, 3, 4, 5]) is a a map set of 5 elements or a map set of one element which is a list of length 5?

1 Like

What do you mean if? MapSet.new/1 accepts enumerables only as it can be easily seen from the function spec.

Then it iterates through this Enumerable, dedups it and produces a MapSet from unique elements in this Enumerable.

2 Likes

I’m aware. The rest of the post includes “I think it’s a little odd that MapSet.new requires that the passed arg be enumerable.” @eksperimental explained why it obviously needs to be that way.