|
4 | 4 | SPDX-FileCopyrightText: 2012 Plataformatec
|
5 | 5 | -->
|
6 | 6 |
|
7 |
| -# Changelog for Elixir v1.19 |
| 7 | +# Changelog for Elixir v1.20 |
8 | 8 |
|
9 | 9 | ## Type system improvements
|
10 | 10 |
|
11 |
| -### More type inference |
| 11 | +### Full type inference |
12 | 12 |
|
13 | 13 | Elixir now performs inference of whole functions. The best way to show the new capabilities are with examples. Take the following code:
|
14 | 14 |
|
|
30 | 30 |
|
31 | 31 | 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.
|
32 | 32 |
|
33 |
| -### Type checking of protocol dispatch and implementations |
34 |
| - |
35 |
| -This release also adds type checking when dispatching and implementing protocols. |
36 |
| - |
37 |
| -For example, string interpolation in Elixir uses the `String.Chars` protocol. If you pass a value that does not implement said protocol, Elixir will now emit a warning accordingly. |
38 |
| - |
39 |
| -Here is an example passing a range, which cannot be converted into a string, to an interpolation: |
40 |
| - |
41 |
| -```elixir |
42 |
| -defmodule Example do |
43 |
| - def my_code(first..last//step = range) do |
44 |
| - "hello #{range}" |
45 |
| - end |
46 |
| -end |
47 |
| -``` |
48 |
| - |
49 |
| -the above emits the following warnings: |
50 |
| - |
51 |
| -``` |
52 |
| -warning: incompatible value given to string interpolation: |
53 |
| -
|
54 |
| - data |
55 |
| -
|
56 |
| -it has type: |
57 |
| -
|
58 |
| - %Range{first: term(), last: term(), step: term()} |
59 |
| -
|
60 |
| -but expected a type that implements the String.Chars protocol, it must be one of: |
61 |
| -
|
62 |
| - dynamic( |
63 |
| - %Date{} or %DateTime{} or %NaiveDateTime{} or %Time{} or %URI{} or %Version{} or |
64 |
| - %Version.Requirement{} |
65 |
| - ) or atom() or binary() or float() or integer() or list(term()) |
66 |
| -``` |
67 |
| - |
68 |
| -Warnings are also emitted if you pass a data type that does not implement the `Enumerable` protocol as a generator to for-comprehensions: |
69 |
| - |
70 |
| -```elixir |
71 |
| -defmodule Example do |
72 |
| - def my_code(%Date{} = date) do |
73 |
| - for(x <- date, do: x) |
74 |
| - end |
75 |
| -end |
76 |
| -``` |
77 |
| - |
78 |
| -will emit: |
79 |
| - |
80 |
| -``` |
81 |
| -warning: incompatible value given to for-comprehension: |
82 |
| -
|
83 |
| - x <- date |
84 |
| -
|
85 |
| -it has type: |
86 |
| -
|
87 |
| - %Date{year: term(), month: term(), day: term(), calendar: term()} |
88 |
| -
|
89 |
| -but expected a type that implements the Enumerable protocol, it must be one of: |
90 |
| -
|
91 |
| - dynamic( |
92 |
| - %Date.Range{} or %File.Stream{} or %GenEvent.Stream{} or %HashDict{} or %HashSet{} or |
93 |
| - %IO.Stream{} or %MapSet{} or %Range{} or %Stream{} |
94 |
| - ) or fun() or list(term()) or non_struct_map() |
95 |
| -``` |
96 |
| - |
97 |
| -### Type checking and inference of anonymous functions |
98 |
| - |
99 |
| -Elixir v1.19 can now type infer and type check anonymous functions. Here is a trivial example: |
100 |
| - |
101 |
| -```elixir |
102 |
| -defmodule Example do |
103 |
| - def run do |
104 |
| - fun = fn %{} -> :map end |
105 |
| - fun.("hello") |
106 |
| - end |
107 |
| -end |
108 |
| -``` |
109 |
| - |
110 |
| -The example above has an obvious typing violation, as the anonymous function expects a map but a string is given. With Elixir v1.19, the following warning is now printed: |
111 |
| - |
112 |
| -``` |
113 |
| - warning: incompatible types given on function application: |
114 |
| -
|
115 |
| - fun.("hello") |
116 |
| -
|
117 |
| - given types: |
118 |
| -
|
119 |
| - binary() |
120 |
| -
|
121 |
| - but function has type: |
122 |
| -
|
123 |
| - (dynamic(map()) -> :map) |
124 |
| -
|
125 |
| - typing violation found at: |
126 |
| - │ |
127 |
| - 6 │ fun.("hello") |
128 |
| - │ ~ |
129 |
| - │ |
130 |
| - └─ mod.exs:6:8: Example.run/0 |
131 |
| -``` |
132 |
| - |
133 |
| -Function captures, such as `&String.to_integer/1`, will also propagate the type as of Elixir v1.19, arising more opportunity for Elixir's type system to catch bugs in our programs. |
134 |
| - |
135 | 33 | ### Acknowledgements
|
136 | 34 |
|
137 | 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/), [Starfish*](https://starfish.team/), and [Dashbit](https://dashbit.co/).
|
138 | 36 |
|
139 |
| -## Faster compile times in large projects |
140 |
| - |
141 |
| -This release includes two compiler improvements that can lead up to 4x faster builds in large codebases. |
142 |
| - |
143 |
| -While Elixir has always compiled the given files in project or a dependency in parallel, the compiler would sometimes be unable to use all of the machine resources efficiently. This release addresses two common limitations, delivering performance improvements that scale with codebase size and available CPU cores. |
144 |
| - |
145 |
| -### Code loading bottlenecks |
146 |
| - |
147 |
| -Prior to this release, Elixir would load modules as soon as they were defined. However, because the Erlang part of code loading happens within a single process (the code server), this would make it a bottleneck, reducing the amount of parallelization, especially on large projects. |
148 |
| - |
149 |
| -This release makes it so modules are loaded lazily. This reduces the pressure on the code server, making compilation up to 2x faster for large projects, and also reduces the overall amount of work done during compilation. |
150 |
| - |
151 |
| -Implementation wise, [the parallel compiler already acts as a mechanism to resolve modules during compilation](https://elixir-lang.org/blog/2012/04/24/a-peek-inside-elixir-s-parallel-compiler/), so we built on that. By making sure the compiler controls both module compilation and module loading, it can also better guarantee deterministic builds. |
152 |
| - |
153 |
| -The only potential regression in this approach happens if you have a module, which is used at compile time and defines an `@on_load` callback (typically used for [NIFs](https://www.erlang.org/doc/system/nif.html)) that invokes another modules within the same project. For example: |
154 |
| - |
155 |
| -```elixir |
156 |
| -defmodule MyLib.SomeModule do |
157 |
| - @on_load :init |
158 |
| - |
159 |
| - def init do |
160 |
| - MyLib.AnotherModule.do_something() |
161 |
| - end |
162 |
| - |
163 |
| - def something_else do |
164 |
| - ... |
165 |
| - end |
166 |
| -end |
167 |
| - |
168 |
| -MyLib.SomeModule.something_else() |
169 |
| -``` |
170 |
| - |
171 |
| -The reason this fails is because `@on_load` callbacks are invoked within the code server and therefore they have limited ability to load additional modules. It is generally advisable to limit invocation of external modules during `@on_load` callbacks but, in case it is strictly necessary, you can set `@compile {:autoload, true}` in the invoked module to address this issue in a forward and backwards compatible manner. |
172 |
| - |
173 |
| -### Parallel compilation of dependencies |
174 |
| - |
175 |
| -This release introduces a variable called `MIX_OS_DEPS_COMPILE_PARTITION_COUNT`, which instructs `mix deps.compile` to compile dependencies in parallel. |
176 |
| - |
177 |
| -While fetching dependencies and compiling individual Elixir dependencies already happened in parallel, there were pathological cases where performance would be left on the table, such as compiling dependencies with native code or dependencies where one or two large file would take over most of the compilation time. |
178 |
| - |
179 |
| -By setting `MIX_OS_DEPS_COMPILE_PARTITION_COUNT` to a number greater than 1, Mix will now compile multiple dependencies at the same time, using separate OS processes. Empirical testing shows that setting it to half of the number of cores on your machine is enough to maximize resource usage. The exact speed up will depend on the number of dependencies and the number of machine cores, although some reports mention up to 4x faster compilation times. If you plan to enable it on CI or build servers, keep in mind it will most likely have a direct impact on memory usage too. |
180 |
| - |
181 |
| -## Improved pretty printing algorithm |
182 |
| - |
183 |
| -Elixir v1.19 ships with a new pretty printing implementation that tracks limits as a whole, instead of per depth. Previous versions would track limits per depth. For example, if you had a list of lists of 4 elements and a limit of 5, it would be pretty printed as follows: |
184 |
| - |
185 |
| -```elixir |
186 |
| -[ |
187 |
| - [1, 2, 3], |
188 |
| - [1, 2, ...], |
189 |
| - [1, ...], |
190 |
| - [...], |
191 |
| - ... |
192 |
| -] |
193 |
| -``` |
194 |
| - |
195 |
| -This allows for more information to be shown at different nesting levels, which is useful for complex data structures. But it led to some pathological cases where the `limit` option had little effect on actually filtering the amount of data shown. The new implementation decouples the limit handling from depth, decreasing it as it goes. Therefore, the list above with the same limit in Elixir v1.19 is now printed as: |
196 |
| - |
197 |
| -```elixir |
198 |
| -[ |
199 |
| - [1, 2, 3], |
200 |
| - ... |
201 |
| -] |
202 |
| -``` |
203 |
| - |
204 |
| -The outer list is the first element, the first nested list is the second, followed by three numbers, reaching the limit. This gives developers more precise control over pretty printing. |
205 |
| - |
206 |
| -Given this may reduce the amount of data printed by default, the default limit has also been increased from 50 to 100. We may further increase it in upcoming releases based on community feedback. |
207 |
| - |
208 |
| -## OpenChain certification |
209 |
| - |
210 |
| -Elixir v1.19 is also our first release following OpenChain compliance, [as previously announced](https://elixir-lang.org/blog/2025/02/26/elixir-openchain-certification/). In a nutshell: |
211 |
| - |
212 |
| - * Elixir releases now include a Source SBoM in CycloneDX 1.6 or later and SPDX 2.3 or later formats. |
213 |
| - * Each release is attested along with the Source SBoM. |
214 |
| - |
215 |
| -These additions offer greater transparency into the components and licenses of each release, supporting more rigorous supply chain requirements. |
216 |
| - |
217 |
| -This work was performed by Jonatan Männchen and sponsored by the Erlang Ecosystem Foundation. |
218 |
| - |
219 |
| -## v1.19.0-dev |
| 37 | +## v1.20.0-dev |
220 | 38 |
|
221 | 39 | ### 1. Enhancements
|
222 | 40 |
|
223 |
| -#### Elixir |
224 |
| - |
225 |
| - * [Access] Add `Access.values/0` for traversing maps and keyword lists values |
226 |
| - * [Base] Add functions to verify if an encoding is valid, such as `valid16?`, `valid64?`, and so forth |
227 |
| - * [Calendar] Support 2-arity options for `Calendar.strftime/3` which receives the whole data type |
228 |
| - * [Code] Add `:migrate_call_parens_on_pipe` formatter option |
229 |
| - * [Code] Add `:indentation` option to `Code.string_to_quoted/2` |
230 |
| - * [Code.Fragment] Preserve more block content around cursor in `container_cursor_to_quoted` |
231 |
| - * [Code.Fragment] Add `:block_keyword_or_binary_operator` to `Code.Fragment` for more precise suggestions after operators and closing terminators |
232 |
| - * [Code.Fragment] Add `Code.Fragment.lines/1` |
233 |
| - * [Enum] Provide more information on `Enum.OutOfBoundsError` |
234 |
| - * [Inspect] Allow `optional: :all` when deriving Inspect |
235 |
| - * [Inspect.Algebra] Add optimistic/pessimistic groups as a simplified implementation of `next_break_fits` |
236 |
| - * [IO.ANSI] Add ANSI codes to turn off conceal and crossed_out |
237 |
| - * [Kernel] Allow controlling which applications are used during inference |
238 |
| - * [Kernel] Support `min/2` and `max/2` as guards |
239 |
| - * [Kernel.ParallelCompiler] Add `each_long_verification_threshold` which invokes a callback when type checking a module takes too long |
240 |
| - * [Kernel.ParallelCompiler] Include lines in `== Compilation error in file ... ==` slogans |
241 |
| - * [Macro] Print debugging results from `Macro.dbg/3` as they happen, instead of once at the end |
242 |
| - * [Module] Do not automatically load modules after their compilation, guaranteeing a more consistent compile time experience and drastically improving compilation times |
243 |
| - * [Protocol] Type checking of protocols dispatch and implementations |
244 |
| - * [Regex] Add `Regex.to_embed/2` which returns an embeddable representation of regex in another regex |
245 |
| - * [String] Add `String.count/2` to count occurrences of a pattern |
246 |
| - |
247 |
| -#### ExUnit |
248 |
| - |
249 |
| - * [ExUnit.CaptureLog] Parallelize log dispatch when multiple processes are capturing log |
250 |
| - * [ExUnit.Case] Add `:test_group` to the test context |
251 |
| - * [ExUnit.Doctest] Support ellipsis in doctest exceptions to match the remaining of the exception |
252 |
| - * [ExUnit.Doctest] Add `:inspect_opts` option for doctest |
253 |
| - |
254 |
| -#### IEx |
255 |
| - |
256 |
| - * [IEx] Support multi-line prompts (due to this feature, `:continuation_prompt` and `:alive_continuation_prompt` are no longer supported as IEx configuration) |
257 |
| - * [IEx.Autocomplete] Functions annotated with `@doc group: "Name"` metadata will appear within their own groups in autocompletion |
258 |
| - |
259 |
| -#### Mix |
260 |
| - |
261 |
| - * [mix] Add support for `MIX_PROFILE_FLAGS` to configure `MIX_PROFILE` |
262 |
| - * [mix compile] Debug the compiler and type checker PID when `MIX_DEBUG=1` and compilation/verification thresholds are met |
263 |
| - * [mix compile] Add `Mix.Tasks.Compiler.reenable/1` |
264 |
| - * [mix deps.compile] Support `MIX_OS_DEPS_COMPILE_PARTITION_COUNT` for compiling deps concurrently across multiple operating system processes |
265 |
| - * [mix help] Add `mix help Mod`, `mix help :mod`, `mix help Mod.fun` and `mix help Mod.fun/arity` |
266 |
| - * [mix test] Allow to distinguish the exit status between warnings as errors and test failures |
267 |
| - * [mix xref graph] Add support for `--format json` |
268 |
| - * [mix xref graph] Emit a warning if `--source` is part of a cycle |
269 |
| - * [M ix.Task.Compiler] Add `Mix.Task.Compiler.run/2` |
270 |
| - |
271 | 41 | ### 2. Bug fixes
|
272 | 42 |
|
273 |
| -#### Elixir |
274 |
| - |
275 |
| - * [DateTime] Do not truncate microseconds regardless of precision in `DateTime.diff/3` |
276 |
| - * [File] Properly handle permissions errors cascading from parent in `File.mkdir_p/1` |
277 |
| - * [Kernel] `not_a_map.key` now raises `BadMapError` for consistency with other map operations |
278 |
| - * [Regex] Fix `Regex.split/2` returning too many results when the chunk being split on was empty (which can happen when using features such as `/K`) |
279 |
| - * [Stream] Ensure `Stream.transform/5` respects suspend command when its inner stream halts |
280 |
| - * [URI] Several fixes to `URI.merge/2` related to trailing slashes, trailing dots, and hostless base URIs |
281 |
| - |
282 |
| -#### Mix |
283 |
| - |
284 |
| - * [mix cmd] Preserve argument quoting in subcommands |
285 |
| - * [mix format] Ensure the formatter does not go over the specified limit in certain corner cases |
286 |
| - * [mix release] Fix `RELEASE_SYS_CONFIG` for Windows 11 |
287 |
| - * [mix test] Preserve files with no longer filter on `mix test` |
288 |
| - * [mix xref graph] Provide more consistent output by considering strong connected components only when computing graphs |
289 |
| - |
290 | 43 | ### 3. Soft deprecations (no warnings emitted)
|
291 | 44 |
|
292 |
| -#### Elixir |
293 |
| - |
294 |
| - * [Inspect.Algebra] `next_break_fits` is deprecated in favor of `optimistic`/`pessimistic` groups |
295 |
| - * [Node] `Node.start/2-3` is deprecated in favor of `Node.start/2` with a keyword list |
296 |
| - |
297 |
| -#### Mix |
298 |
| - |
299 |
| - * [mix compile] `--no-protocol-consolidation` is deprecated in favor of `--no-consolidate-protocols` for consistency with `mix.exs` configuration |
300 |
| - * [mix compile.protocols] Protocol consolidation is now part of `compile.elixir` and has no effect |
301 |
| - |
302 | 45 | ### 4. Hard deprecations
|
303 | 46 |
|
304 |
| -#### Elixir |
305 |
| - |
306 |
| - * [Code] The `on_undefined_variable: :warn` is deprecated. Relying on undefined variables becoming function calls will not be supported in the future |
307 |
| - * [File] Passing a callback as third argument to `File.cp/3` is deprecated, pass it as a `on_conflict: callback` option instead |
308 |
| - * [File] Passing a callback as third argument to `File.cp_r/3` is deprecated, pass it as a `on_conflict: callback` option instead |
309 |
| - * [Kernel] The struct update syntax, such as `%URI{uri | path: "/foo/bar"}` is deprecated in favor of pattern matching on the struct when the variable is defined and then using the map update syntax `%{uri | path: "/foo/bar"}`. Thanks to the type system, pattern matching on structs can find more errors, more reliably |
310 |
| - * [Kernel.ParallelCompiler] Passing `return_diagnostics: true` as an option is required on `compile`, `compile_to_path` and `require` |
311 |
| - |
312 |
| -#### Logger |
313 |
| - |
314 |
| - * [Logger] The `:backends` configuration is deprecated, either set the `:default_handler` to false or start backends in your application start callback |
315 |
| - |
316 |
| -#### Mix |
317 |
| - |
318 |
| - * [mix] The `:default_task`, `:preferred_cli_env`, and `:preferred_cli_target` configuration inside `def project` in your `mix.exs` has been deprecated in favor of `:default_task`, `:preferred_envs` and `:preferred_targets` inside the `def cli` function |
319 |
| - * [mix do] Using commas as task separator in `mix do` (such as `mix do foo, bar`) is deprecated, use `+` instead (as in `mix do foo + bar`) |
320 |
| - |
321 |
| -## v1.18 |
| 47 | +## v1.19 |
322 | 48 |
|
323 |
| -The CHANGELOG for v1.18 releases can be found [in the v1.18 branch](https://github.com/elixir-lang/elixir/blob/v1.18/CHANGELOG.md). |
| 49 | +The CHANGELOG for v1.19 releases can be found [in the v1.19 branch](https://github.com/elixir-lang/elixir/blob/v1.19/CHANGELOG.md). |
0 commit comments