dwark

dwark

The following code results in a warning that is difficult for me to understand:

defmodule Mod do
  def hello(name) do
    map = Map.new(words: ["world"])

    IO.inspect(length(map.words), label: :before)
    map = %{map | words: [name | map.words]}
    IO.inspect(length(map.words), label: :after)
  end
end

This yields the following warning:

warning: incompatible types:

    %{words: [var1 | var2]} !~ %{words: var2}

in expression:

    # lib/mod.ex:7
    map.words

where "map" was given the type %{words: [var1 | var2], words: var2, optional(dynamic()) => dynamic()} in:

    # lib/mod.ex:6
    map = %{map | words: [name | map.words]}

where "map" was given the type %{words: [var1 | var2], optional(dynamic()) => dynamic()} (due to calling var.field) in:

    # lib/mod.ex:7
    map.words

HINT: "var.field" (without parentheses) implies "var" is a map() while "var.fun()" (with parentheses) implies "var" is an atom()

Conflict found at
  lib/mod.ex:7

The warning goes away if:

  1. The first IO.inspect with label: :before is commented out. Then the following two
    lines (6 and 7) are not longer at odds with each other type-wise.

  2. OR: Leave the IO.inspect as-is, but change the line following it (the map update) to
    one of the following:

map = %{map | words: [name] ++ map.words}
# or
map = %{map | words: [name | map[:words]]}
  1. OR: Use a struct instead of a dynamic map.

  2. OR: change the first IO.inspect to inspect map[:words] instead of map.words

So the warning is about conflicting types for %{words: [var1 | var2]} !~ %{words: var2}

  • line 6 → %{words: [var1 | var2], words: var2, optional(dynamic()) => dynamic()}
  • line 7 → %{words: [var1 | var2], optional(dynamic()) => dynamic()}

both mention optional(dynamic()) => dynamic(), the first seemingly only for var2 in the prepending of the list, the latter for the list itself?

In both cases, map is a (dynamic) map, the atom key :words’s value is a list so I expected
both to be of the same type, since [var1 | var2] is prepending var1 to the list var2,
much like the [var1] ++ var2 variant. This is what confuses me.

It all seems related to using the dot-notation to access an atom key in a dynamic map.

The docs say:

To access atom keys, one may also use the map.key notation. Note that map.key will raise a KeyError if the map doesn’t contain the key :key , compared to map[:key] , that would return nil .

as well as:

The two syntaxes for accessing keys reveal the dual nature of maps. The map[key] syntax is used for dynamically created maps that may have any key, of any type. map.key is used with maps that hold a predetermined set of atoms keys, which are expected to always be present. Structs, defined via defstruct/1, are one example of such “static maps”, where the keys can also be checked during compile time.

So should I read the above to always use map[key] for dynamic maps even if the key is an atom, and map.key only for static maps like a struct?

Showing Posts 1 to 3

sabiwara

sabiwara

Elixir Core Team

The code seems fine, I think this one is a bug in the compiler.

Opened an issue: Incorrect incompatible type warning on map dot access · Issue #13335 · elixir-lang/elixir · GitHub

dwark

dwark OP

Ok, thanks.

dwark

dwark OP

For future readers: this was already fixed in 1.16 (I was on 1.15.7).

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kszambelanczyk
Hello! Could someone please give me a help/sample code, how to delete a file from s3 using waffle/waffle_ecto from Phoenix app. I creat...
New
RemyXRenard
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
samoloth
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
FlyingNoodle
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
ryanwinchester
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted” Version...
New

Other Trending Topics Top

mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
garrison
Hobbes is a low-level distributed database for the Elixir programming language. Hobbes provides a simple, safe, and scalable storage lay...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve. They are GUI (Emerge) and State management (S...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews