Skip to content

Commit 743fea7

Browse files
committed
Update CHANGELOG
1 parent 67baf68 commit 743fea7

File tree

1 file changed

+91
-1
lines changed

1 file changed

+91
-1
lines changed

CHANGELOG.md

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,105 @@ end
3030

3131
Even though the `+` operator works with both integers and floats, Elixir infers that `a` and `b` must be both integers, as the result of `+` is given to a function that expects an integer. The inferred type information is then used during type checking to find possible typing errors.
3232

33+
### Complete typing of maps keys
34+
35+
Maps were one of the first data-structures we implemented within the Elixir type system however, up to this point, they only supported atom keys. If they had additional keys, those keys were simply marked as `dynamic()`.
36+
37+
As of Elixir v1.20, we can track all possible domains as map keys. For example, the map:
38+
39+
```elixir
40+
%{123 => "hello", 456.0 => :ok}
41+
```
42+
43+
will have the type:
44+
45+
```elixir
46+
%{integer() => binary(), float() => :ok}
47+
```
48+
49+
It is also possible to mix domain keys, as above, with atom keys, yielding the following:
50+
51+
```elixir
52+
%{integer() => integer(), root: integer()}
53+
```
54+
55+
This system is an implementation of [Polymorphic Records for Dynamic Languages, by Giuseppe Castagna and Loïc Peyrot (2024)](https://arxiv.org/abs/2404.00338).
56+
57+
### Typing of map operations
58+
59+
We have typed the majority of the functions in the `Map` module, allowing the type system to track how keys are added, updated, and removed across all possible key types.
60+
61+
For example, imagine we are calling the following `Map` functions with a variable `map`, which we don't know the exact shape of, and an atom key:
62+
63+
```elixir
64+
Map.put(map, :key, 123)
65+
#=> returns type %{..., key: integer()}
66+
67+
Map.delete(map, :key)
68+
#=> returns type %{..., key: not_set()}
69+
```
70+
71+
As you can see, when track when keys are set and also when they are removed.
72+
73+
Some operations, like `Map.replace/3`, only replace the key if it exists, and that is also propagated by the type system:
74+
75+
```elixir
76+
Map.replace(map, :key, 123)
77+
#=> returns type %{..., key: if_set(integer())}
78+
```
79+
80+
In other words, if the key exists, it would have been replaced by an integer value. Furthermore, whenever calling a function in the `Map` module and the given key is statically proven to never exist in the map, an error is emitted.
81+
82+
By combining full type inference with bang operations like `Map.fetch!/2`, `Map.pop!/2`, `Map.replace!/2`, and `Map.update!/3`, Elixir is able to propagate information about the desired keys. Take this module:
83+
84+
```elixir
85+
defmodule User do
86+
def name(map), do: Map.fetch!(map, :name)
87+
end
88+
89+
defmodule CallsUser do
90+
def calls_name do
91+
User.name(%{})
92+
end
93+
end
94+
```
95+
96+
The code above has a type violation, which is now caught by the type system:
97+
98+
```
99+
warning: incompatible types given to User.name/1:
100+
101+
User.name(%{})
102+
103+
given types:
104+
105+
%{name: not_set()}
106+
107+
but expected one of:
108+
109+
dynamic(%{..., name: term()})
110+
111+
typing violation found at:
112+
113+
16 │ User.name(%{})
114+
│ ~
115+
116+
└─ lib/calls_user.ex:7:5: CallsUser.calls_name/0
117+
```
118+
119+
Once again, our goal is to propagate type information and help developers find bugs in their code, without a need to introduce type signatures (yet).
120+
33121
### Acknowledgements
34122

35-
The type system was made possible thanks to a partnership between [CNRS](https://www.cnrs.fr/) and [Remote](https://remote.com/). The development work is currently sponsored by [Fresha](https://www.fresha.com/) and [Dashbit](https://dashbit.co/).
123+
The type system was made possible thanks to a partnership between [CNRS](https://www.cnrs.fr/) and [Remote](https://remote.com/). The development work is currently sponsored by [Fresha](https://www.fresha.com/) and [Tidewave](https://tidewave.ai/).
36124

37125
## v1.20.0-dev
38126

39127
### 1. Enhancements
40128

41129
#### Elixir
42130

131+
* [Calendar] Optimize `date_from_iso_days` by using the Neri-Schneider algorithm
43132
* [Enum] Add `Enum.min_max` sorter
44133
* [Integer] Add `Integer.ceil_div/2`
45134
* [Kernel] Print intermediate results of `dbg` for pipes
@@ -52,6 +141,7 @@ The type system was made possible thanks to a partnership between [CNRS](https:/
52141

53142
#### Mix
54143

144+
* [mix deps] Support filtering `mix deps` output
55145
* [mix test] Add `mix test --dry-run`
56146

57147
### 2. Bug fixes

0 commit comments

Comments
 (0)