You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+91-28Lines changed: 91 additions & 28 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -26,71 +26,134 @@ See the full CHANGELOG for more improvements.
26
26
27
27
## Improvements to sort-based APIs in Enum
28
28
29
-
`Enum.sort/1` in Elixir always sorts from lowest to highest. If you want to sort from highest to lowest, you need to call `Enum.sort/2` with a custom sorting function, such as `Enum.sort(collection, &>=/2)`, which is not immediately obvious to someone reading the code.
29
+
`Enum.sort/1` in Elixir by default sorts from lowest to highest:
30
30
31
-
To make matters worse, comparison operators, such as `<=` and `>=`, perform structural sorting, instead of a semantic one. For example, using `>=` to sort dates descendingly won't yield the correct result. Therefore, to sort dates from more recent to oldest, one has to write `Enum.sort(dates, &(Date.compare(&1, &2) != :lt))`.
31
+
```elixir
32
+
iex>Enum.sort(["banana", "apple", "pineapple"])
33
+
["apple", "banana", "pineapple"]
34
+
```
35
+
36
+
If you want to sort from highest to lowest, you need to call `Enum.sort/2` with a custom sorting function, such as `Enum.sort(collection, &>=/2)`, which is not immediately obvious to someone reading the code:
Furthermore, comparison operators, such as `<=` and `>=`, perform structural sorting, instead of a semantic one. For example, using `>=` to sort dates descendingly won't yield the correct result:
44
+
45
+
```elixir
46
+
iex>Enum.sort([~D[2019-12-31], ~D[2020-01-01]])
47
+
[~D[2020-01-01], ~D[2019-12-31]]
48
+
```
49
+
50
+
To perform proper semantic comparison for dates, one would also need to pass a custom sorting function:
Furthermore, if you want to perform semantic comparison, you can pass a module that provides the relevant comparison function. For example, to sort dates:
66
+
As well as adding the possibility to pass a module to perform semantic comparisons. For example, to sort dates, one now only needs to pass the `Date` module or even `{:desc, Date}` for descending semantical sort:
This new API has also been added to `Enum.sort_by`, `Enum.min_by`, `Enum.max_by`, and friends.
75
+
These API improvements make the code more concise and readable and they have also been added to `Enum.sort_by`, `Enum.min_by`, `Enum.max_by`, and friends.
45
76
46
77
### Tracking of compile-time configuration
47
78
48
-
All applications in Elixir come with an application environment. This environment is a key-value store that allows us to configure said application. While reading the application environment at runtime is the preferred approach, in some rare occasions you may want to use the application environment to configure the compilation of a certain project. This is often done by calling `Application.get_env/3` outside of a function:
79
+
In Elixir, we organize our code in applications. Libraries, your dependencies, and your own project are all separate applications. All applications in Elixir also come with an application environment.
The application environment is a key-value store that allows us to configure said application. While reading the application environment at runtime is the preferred approach, in some rare occasions you may want to use the application environment to configure the compilation of a certain project. This is often done by calling `Application.get_env/3` outside of a function:
This approach has one big limitation: if you change the value of the application environment after the code is compiled, the value used at runtime is not going to change! For example, if you are using `mix release` and your `config/releases.exs` has:
59
93
60
94
config :my_app, :db_host, "db.production"
61
95
62
-
The new value will have no effect as the code was compiled to connect to "db.local", which is mostly likely unavailable in the production environment.
96
+
Because `config/releases.exs` is read after the code is compiled, the new value will have no effect as the code was compiled to connect to "db.local".
97
+
98
+
Of course, the obvious solution to this mismatch is to not read the application environment at compilation time in the first place, and instead move the code to inside a function:
63
99
64
-
For those reasons, reading the application environment at runtime should be the first choice. However, if you really have to read the application environment during compilation, Elixir v1.10 introduces a `Application.compile_env/3` function:
While this is the preferred approach, there are still two scenarios we need to address:
67
112
68
-
By using `compile_env/3`, Elixir will store the values used during compilation and compare the compilation values with the runtime values whenever your system starts, raising an error in case they differ. This helps developers ensure they are running their production systems with the configuration they intend to.
113
+
1. Not everyone may be aware of this pitfall, so they will mistakenly read the application environemnt at compile-time, until they are bitten by this behaviour
114
+
115
+
2. In rare occasions, you trully need to read the application environment at compile-time, and you want to be warned when you try to configure at runtime something that is valid only at compilation time
116
+
117
+
Elixir v1.10 aims to solve these two scenarios by introducing a `Application.compile_env/3` function. For example, to read the value at compile time, you can now do:
By using `compile_env/3`, Elixir will store the values used during compilation and compare them with the runtime values whenever your system starts, raising an error in case they differ. This helps developers ensure they are running their production systems with the configuration they intend to.
124
+
125
+
In future versions, we will deprecate the use `Application.get_env` at compile-time with a clear message pointing users to configuration best practices, effectively addressing the scenario where users read from the application environment at compile time unaware of its pitfalls.
69
126
70
127
### Compiler tracing
71
128
72
129
This release brings enhancements to the Elixir compiler and adds new capabilities for developers to listen to compilation events.
73
130
74
-
In previous Elixir releases, Elixir would compile a database of cross references between modules (such as function calls, references, structs, etc) for each project. Although developers could traverse this database, they often requested more events or more information to be made available.
131
+
In previous Elixir versions, Elixir would compile a database of cross references between modules (such as function calls, references, structs, etc) for each project in order to perform all kinds of checks, such as deprecations and undefined functions.
75
132
76
-
In Elixir v1.10, we have replaced this database by compiler tracing. This means that developers can now directly listen to events emitted by the compiler to store and collect all the information they need (and only the information they need).
133
+
Although this database was not public, developers would still use it to run their own checks against their projects. With time, developers would request more data to be included in the database, which was problematic as Elixir itself did not have a use for the additional data, and the database was not meant to be used externally in the first place.
77
134
78
-
Elixir itself is already using the new compiler tracing to provide new functionality. In particular, the compiler now checks for undefined function warnings more consistently. In previous versions, we would emit undefined function warnings only for files in `lib`, skipping test files and scripts.
135
+
In Elixir v1.10, we have addressed these problems by introducing compiler tracing. The compiler tracing allows developers to listen to events as they are emitted by the compiler, so they can store all of the information they need - and only the information they need.
79
136
80
-
Furthermore, in Elixir v1.10 developers can now disable undefined function warnings directly on the callsite. For example, imagine you have an optional dependency which may not be available in some cases. You can tell the compiler to skip warning on calls to optional modules with:
137
+
Elixir itself is using the new compiler tracing to provide new functionality. One advantage of this approach is that developers can now disable undefined function warnings directly on the callsite. For example, imagine you have an optional dependency which may not be available in some cases. You can tell the compiler to skip warning on calls to optional modules with:
81
138
82
139
@compile {:no_warn_undefined, OptionalDependency}
83
140
defdelegate my_function_call(arg), to: OptionalDependency
84
141
85
-
Finally, as consequence of these improvements, some functionality related to `xref` (our previous database), has been deprecated in favor of the new compiler tracing.
142
+
Previously, this information had to be added to the overall project configuration, which was far away from where the optional call effectively happened.
86
143
87
144
### Other enhancements
88
145
89
-
The calendar data types got many improvements, such as sigil support for third-party calendars, as well as the additions of `DateTime.now!/2`, `DateTime.shift_zone!/3`, and `NaiveDateTime.local_now/0`.
146
+
Elixir's calendar data types got many improvements, such as sigil support for third-party calendars, as well as the additions of `DateTime.now!/2`, `DateTime.shift_zone!/3`, and `NaiveDateTime.local_now/0`.
147
+
148
+
There are many improvements related to Elixir's AST in this release too. First of all, `Code.string_to_quoted/2` has two new options, `:token_metadata` and `:literal_encoder`, that give more control over Elixir's parser. This information was already available to the Elixir code formatter and has now been made public. We have also extensively documented all of Elixir's AST metadata. These changes alongside compiler tracing means static analyzers and IDE integrations have a better foundation to analyze the source code.
149
+
150
+
ExUnit, our test framework, ships two small but important improvements: `ExUnit.CaptureIO` can now be used by tests that run concurrently and we have added "pattern-matching diffing". To understand the last feature, take this code:
90
151
91
-
There are many improvements related to the Elixir AST in this release too. First of all, `Code.string_to_quoted/2` has two new options, `:token_metadata` and `:literal_encoder`, that give more control over Elixir's parser. This information has already been available to the Elixir formatter for a couple versions and has now been made public. Furthermore, all public metadata entries in the AST nodes have been extensively documented. These changes alongside the compiler improvements from previous section means tools like Credo and Boundary now have a better foundation to analyze the source code.
Finally, ExUnit comes with two small but important improvements: `ExUnit.CaptureIO` can now be used in tests that run asynchronously and we have added "data-structure diffing" when performing assertions with pattern matching. So now, whenever an assertion such `assert %{field: value} = expression()` fails, ExUnit will show both left-hand and right-hand sides, highlighting the parts that did not match in red.
156
+
Now imagine that `json_payload` is a large JSON blob and the `"key"` inside the `"body"` did not have value of `"foo"`. In previous Elixir versions, if the assertion failed, Elixir would print the right side and let you up to your own devices to figure out what went wrong. In Elixir v1.10, we diff the data structure against the pattern so you can see exactly which parts of the data matched the pattern and which ones did not. Note ExUnit already performed diffing when comparing data types, this new version adds diffing when matching data agaainst a pattern.
0 commit comments